首页 > 其他分享 >【QBXT 2023 冬】NOIP 突破营 补题清单

【QBXT 2023 冬】NOIP 突破营 补题清单

时间:2024-02-28 15:36:56浏览次数:30  
标签:10 48 NOIP int top QBXT 补题 sum dp

题单:

第一部分

第二部分

题解有时间就写,一般会咕。

P5691 [NOI2001] 方程的解数

简单的折半搜索。

直接搜索时间复杂度是 \(O(m^6)-O(m^6\log p_i)\) 的(快速幂),无法通过。

考虑优化,首先我们对上面的式子做一个变形:

\[\sum_{i=1}^{n}k_ix_i^{p_i}=0 \]

\[\sum_{i=1}^{\lfloor\frac{n}{2}\rfloor}k_ix_i^{p_i}+\sum_{i=\lfloor\frac{n}{2}\rfloor}^{n}k_ix_i^{p_i}=0 \]

\[-\sum_{i=1}^{\lfloor\frac{n}{2}\rfloor}k_ix_i^{p_i}=\sum_{i=\lfloor\frac{n}{2}\rfloor}^{n}k_ix_i^{p_i} \]

那么我们只需要对 \([1,\lfloor\frac{n}{2}\rfloor]\) 和 \([\lfloor\frac{n}{2}\rfloor+1,n]\) 两段分别进行搜索即可。考虑到每种方案所选的未知数都不同,所以搜出几个数就存在几种方案。

时间复杂度 \(O(m^3\log p_i)\),可以通过。

优化:此题不卡模数,将 map 更换为 unordered_map 并预处理幂次就可以去掉那一只 \(\log\)。

代码(不带优化):

#include<bits/stdc++.h>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
#define y1 y233
typedef long long ll;
//typedef __int128 lll;
typedef unsigned long long ull;
const int N=15;
using namespace std;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline int read(){
   	int x=0;bool sgn=0;char ch=gt();
   	while(!__(ch)&&ch!=EOF){sgn|=(ch=='-');ch=gt();}
   	while(__(ch)){x=(x<<1)+(x<<3)+(ch-48);ch=gt();}
	return sgn?-x:x;
}
template<class T>
inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);
}
template<class T>
inline void printsp(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);pt(32);
}
template<class T>
inline void println(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);pt(10);
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz;++i) pt(s[i]);
	printf("\n");
}
int n,m,k[N],p[N],ans;
unordered_map<int,int>mp;
inline int power(int a,int b){
	int res=1;
	while(b){
		if(b&1)res*=a;
		a*=a,b>>=1;
	}
	return res;
}
void dfs1(int x,int sum){
	// 1 to [n/2]
	if(x>n/2){
		mp[sum]++;
		return; 
	}
	for(int i=1;i<=m;++i) dfs1(x+1,sum+k[x]*power(i,p[x]));
}
void dfs2(int x,int sum){
	// [n/2+1] to n
	if(x>n){
		ans+=mp[-sum];
		return; 
	}
	for(int i=1;i<=m;++i) dfs2(x+1,sum+k[x]*power(i,p[x]));
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i) k[i]=read(),p[i]=read();
	dfs1(1,0);
	dfs2(n/2+1,0);
	println(ans);
	return 0;
}

P1763 埃及分数

P1379 八数码难题

one - 模拟赛 D1T1

two - 模拟赛 D1T2

three - 模拟赛 D1T3

four - 模拟赛 D1T4

P3195 [HNOI2008]玩具装箱

很好的一道题。

首先考虑暴力 dp。

设:\(dp_i\) 表示考虑前 \(i\) 个元素所能获得的最小费用。

转移:考虑枚举 \(i\) 上一个区间的右端点,得

\[dp_i=\min_{j=0}^{i-1}dp_j+(i-j-1+\sum_{k=j+1}^{i}a_k-L)^2 \]

注:这里用 \(a\) 数组来代替原题中的 \(c\) 数组。

这样朴素实现是 \(O(n^3)\) 的,使用前缀和优化即可做到 \(O(n^2)\),可以获得 \(70\) 分。

考虑优化,首先我们来推一下式子:

\[dp_i=\min_{j=0}^{i-1}dp_j+(i-j-1+\sum_{k=j+1}^{i}a_k-L)^2 \]

记 \(sum_i=\sum_{j=1}^{i}a_j\),得:

\[dp_i=\min_{j=0}^{i-1}dp_j+(i-j-1+sum_i-sum_j-L)^2 \]

实际算的时候这样就可以了,但是为了发掘更多的性质,我们还要继续推:

记 \(q_i=sum_i+i\),得:

\[dp_i=\min_{j=0}^{i-1}dp_j+[q_i-q_j-(L+1)]^2 \]

展开,得:

\[dp_i=\min_{j=0}^{i-1}dp_j+q_i^2-2q_iL-2q_i+(L+1)^2-2q_iq_j+q_j^2+2q_jL+2q_j \]

容易发现前面的一大堆都与 \(j\) 无关,我们令 \(K=q_i^2-2q_iL-2q_i+(L+1)^2\),然后提出来:

\[dp_i=K+\min_{j=0}^{i-1}dp_j-2q_iq_j+q_j^2+2q_jL+2q_j \]

令 \(k_j=-2q_j,x=q_i-L-1,b_j=dp_j+q_j^2\),得:

\[dp_i=K+\min_{j=0}^{i-1}k_jx+b_j \]

类似的,这个式子还可以转换为斜率优化的标准形式,然后维护凸包之类的东西。但是由于我不会斜率优化,这里就先不讲了。/kk

观察上面的式子,首先 \(K\) 是统一加上的,不用管,而剩下的就是 \(y=kx+b\) 的标准一元一次函数形式。

容易发现其实我们要求的是 \(y\),所以实际上就是求 \(x=q_i-L-1\) 这条线与若干条(实际上有 \(i\) 条)形如 \(y=k_jx+b_j\) 的直线的交点的纵坐标最小值。

然后我们就可以直接维护半平面交了。但是我还是不会,所以不会讲。

继续观察式子:由于 \(a_i\) 和 \(i\) 都大于 \(0\),所以 \(q_i\) 单调递增。再代入原式,得 \(k_j<0\) 且 \(k_j\) 单调递减,\(x\) 递增。

\(k_j<0\) 且单调递减带来一个很好的性质是,编号更大的直线一定比更小的直线斜率更大。

由于我们并不想求凸包或者半平面交,因此我们考虑使用二分栈

记 \(z_i\) 表示 \(dp_i\) 的最优转移点,那么有一个很重要的性质:\(z_i\) 单调不降。

考虑证明,我们使用 GeoGebra 画图:

(其实我不太会用这个东西)

如图,蓝线为 \(i\),绿线为 \(j\)(编号),交点为 \(A\),且 \(j<i\)。

由于我们要求的是纵坐标最小值,那么容易发现横坐标在 \(A\) 左边时 \(j\) 更优,而在右边时 \(i\) 更优。由于横坐标 \(x=q_i-L-1\) 递增,所以 \(A\) 左侧的编号也必然小于其右侧的编号,也就证明了 \(z\) 单调不降。

这个时候我们发现 \(z\) 满足单调性,自然就可以二分了。

初始默认最优转移点 \(z=0\),然后就可以发现,每算出一个 \(dp_i\),就会更新 \(z\) 的一段后缀(单调不降)。因此我们可以用一个栈来维护这些最优转移点所管辖的区间。

具体地,在计算 \(dp_i\) 时,首先将栈内所有 \(r<i\) 的最优转移点弹出,因为它们已经失去了价值。其实只需要弹出一次,因为更前面的决策点已经被 \([1,i-1]\) 中的点弹出了。此时再用栈顶的最优决策点来更新 \(dp_i\) 的值。

现在我们需要用 \(dp_i\) 来更新最优决策点。由于是后缀,所以我们从栈底开始,暴力删除更劣的决策点区间(当前决策点所计算出来的区间左端点的 \(dp\) 值比原来更优)。记删完后 \(i\) 前面的区间为 \(lst\),此时仍然有两种情况:

  • \(i\) 将所有决策点都删掉了,或者 \(i\) 计算出来的 \(lst\) 右端点的决策值不如原本 \(lst\) 计算出来的优(\(lst\) 不会改变)。此时仍然细分成两种情况:

    \(1.\) \(i\) 没有删掉任何一个决策点,此时直接退出(单调不降不意味着每个数都能成为最优决策点)。

    \(2.\) 否则,直接将 \(i\) 插入到 \(lst\) 后面,即栈底即可。管辖区间为 \([lst.r+1,n]\)。

  • \(i\) 计算的右端点值比原本 \(lst\) 计算出来的值更优。此时虽然不会删除 \(lst\) 全部(否则已经删了),但是会删除 \(lst\) 的一部分。记这部分为 \([x,lst.r]\),那么显然 \([lst.l,x-1]\) 这一段都是 \(lst\) 更优。\(x\) 显然可以通过二分查找求出,那么只需要覆盖 \([x,n]\) 这一段后缀即可。

下面我们来计算时间复杂度:

  • 对于每个决策点,其最多入栈出栈一次,时间复杂度 \(O(n)\)。

  • 每个决策点更新后缀的时候需要二分,决策点数量为 \(n\),二分次数是 \(\log n\) 的级别,时间复杂度 \(O(n\log n)\)。

这也解释了为什么暴力删除复杂度是对的。

这样的话时间复杂度就是 \(O(n\log n)\),可以通过。虽然比斜率优化之类的 \(O(n)\) 差一些,但也很不错了。

UPD:二分栈的操作和队列是一样的,但是名字就叫二分栈(不太能理解给这个数据结构取名字的人)。

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
#define y1 y233
#define int long long
typedef long long ll;
//typedef __int128 lll;
typedef unsigned long long ull;
const int N=5e4+5;
using namespace std;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline int read(){
   	int x=0;bool sgn=0;char ch=gt();
   	while(!__(ch)&&ch!=EOF){sgn|=(ch=='-');ch=gt();}
   	while(__(ch)){x=(x<<1)+(x<<3)+(ch-48);ch=gt();}
	return sgn?-x:x;
}
template<class T>
inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);
}
template<class T>
inline void printsp(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);pt(32);
}
template<class T>
inline void println(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);pt(10);
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz;++i) pt(s[i]);
	printf("\n");
}
int n,a[N],L,sum[N],dp[N];
struct Node{
	int id,l,r;
	Node(int _id=0,int _l=0,int _r=0):id(_id),l(_l),r(_r){}
}stk[N];
int head=1,tail=0;
inline int calc(int l,int r){
	if(l>=r)return LONG_LONG_MAX;
	int qwq=sum[r]-sum[l]+r-l-(L+1);
	return dp[l]+qwq*qwq;
}
signed main(){
	n=read(),L=read();
	for(int i=1;i<=n;++i) a[i]=read(),sum[i]=sum[i-1]+a[i];
	stk[++tail]=Node(0,1,n);
	for(int i=1;i<=n;++i){
		if(stk[head].r<i)head++;
		dp[i]=calc(stk[head].id,i);
		bool flag=0;
		while(calc(stk[tail].id,stk[tail].l)>=calc(i,stk[tail].l))tail--,flag=1;
		if(tail<head||calc(stk[tail].id,stk[tail].r)<calc(i,stk[tail].r)){
			if(!flag)continue;
			stk[tail+1]=Node(i,stk[tail].r+1,n),tail++;
		}else{
			int l=stk[tail].l,r=stk[tail].r,ans=0;
			while(l<=r){
				int mid=l+((r-l)>>1);
				if(calc(stk[tail].id,mid)>=calc(i,mid))ans=mid,r=mid-1;
				else l=mid+1;
			}
			stk[tail++].r=ans-1;
			stk[tail]=Node(i,ans,n);
		}
	}
	println(dp[n]);
	return 0;
}

P1040 [NOIP2003 提高组] 加分二叉树

P6419 [COCI2014-2015#1] Kamp

P3177 [HAOI2015] 树上染色

P2279 [HNOI2003]消防局的设立

标签:10,48,NOIP,int,top,QBXT,补题,sum,dp
From: https://www.cnblogs.com/syzqwq/p/18040574

相关文章

  • NOIP2023游记及总结
    Part1游记某学校初一学生,坐标SN,第一次考NOIP,内心紧张无比。Day-5~Day-3期中考试。为了竞赛,政史地生都没背,慌。Day-1期中考试出成绩,被同机房大佬暴甩10.5,明天就要NOIP了,紧张,波波还在训练,晚上写作业到0点,险些失眠。Day17:50进考场前,波波让我们拍照留念,那一刻,我有点想......
  • OI 回忆录/ NOIP 2023 游记
    rt,退役了就更update:应该是退役了。初识最初认识OI应该算是小学,小学到现在就拿个1=确实是小丑了。记得是三年级,学校选了一些眼睛好的数学好的拉去机房练打字,没错,就是练习打字。然后当时考了SD-J组还是X组的初赛我不大清楚,考了两次一次初赛三等一次初赛二等,很小丑。因......
  • 1224本周补题
    P1142轰炸-洛谷|计算机科学教育新生态(luogu.com.cn)似乎数据有点小,三重循环都能过,也可以整个map然后二次循环枚举最后遍历一次也可以,特别注意的是最开始我枚举的斜率,实际上在题目要求之下需要的是三点共线,只是斜率相等并不能满足题意。#include<bits/stdc++.h>usingnam......
  • 1217本周补题
    Dashboard-CodeforcesRound916(Div.3)-CodeforcesA.ProblemsolvingLogcf题意真难懂啊,这题就看字母出现次数一次统计就行,等于就cnt++就行#include<bits/stdc++.h>usingnamespacestd;intmain(){intt;cin>>t;while(t--){intn;......
  • 洛谷题单指南-贪心-P1080 [NOIP2012 提高组] 国王游戏
    原题链接:https://www.luogu.com.cn/problem/P1080题意解读:通过不同的排队方式,让获得最多奖赏的大臣金额尽可能的少。此题如果没有思路,用全排列枚举可以“骗”分,更好的做法直觉上是某种贪心策略,另外基于数据规模考虑,要拿满分,需要上高精度。下面就由浅入深一步一步的解决。解题思......
  • 2024牛客寒假算法基础集训营1(补题)
    目录ABCDEFGHIKLAn的范围很小暴力直接\(O(n^3)\)直接做就行。我还傻的统计了一下前后缀,不过怎么写都行这道题。#include<bits/stdc++.h>#defineintlonglong#definerep(i,a,b)for(inti=(a);i<=(b);++i)#definefep(i,a,b)for(inti=(a);i>=(b);--i)#d......
  • 蒟蒻的2023NOIP游记(非正式)
    前言:这是篇blog这周集中打模拟赛的记录,后会和NOIP场外游记并在一起。11/11双十一,打了两场共同体NOIP模拟赛157:55左右开题t1,t2,t3,t4看了一眼,觉得t1,t2可做想t1,到8:40想出了做法(赛后看来离正解挺近的),9:30左右写好。对于一张n个点的图,由菊花图可想到,应该是对半开。......
  • 洛谷题单指南-贪心-P1094 [NOIP2007 普及组] 纪念品分组
    原题链接:https://www.luogu.com.cn/problem/P1094题意解读:贪心选择解题思路:贪心策略:将纪念品按价格由小到大排序,优先选择价格大的一直到超过分组价格上限,再选择价格小的直到超过价格上限,此为一组重复以上过程,直到所有数据都遍历到,采用一头一尾双指针即可。证明:如果最大价格......
  • 牛客寒假4到6补题
    牛客寒假4:F:来点每日一题题意:给定一个长度为n的数组,任意选6个数,6个数得分为 ((a-b)*c-d)*e-f,问最大能得到多少分解:n*n的dp,暴力枚举每一个数字v[i],f[i]表示以第i个位置结尾的得分最大是多少 voidsolve(){intn;cin>>n;vector<int>v(n+10......
  • 洛谷题单指南-贪心-P5019 [NOIP2018 提高组] 铺设道路
    原题链接:https://www.luogu.com.cn/problem/P5019题意解读:最短时间内填满道路,连在一起的不为0的坑可以一起填解题思路:方法1:分治法对于一段连续不同深度的坑,可以最多连续填的天数是最小深度在填满最小深度之后,分别针对其左边和右边的区域再进行填充,这就是此题分治法的理论基......