Codeforces 286E Ladies' Shop
大致题意:
让你构造尽量少的数,
使得从这些数中, 选出若干个数(可以重复选同一个数,和<=m)它们的和组成的集合,
与输入的n个数的集合完全相同
n,m<=1e6:
本题做法:
首先我们能轻易得出一个结论,
构造的这些数,一定出现在给定的n个数之中。
这个结论是显然的,这里不再赘述。
于是我们得到一个新的题意:
从n个数中选出若干个数,使得它们能够组成的和的集合与原序列完全相同。
以下两个算法都基于这个结论:
算法1(来自cwy的一个tle做法):
考虑贪心。从小到大枚举输入的n个数,
如果当前数无法用之前选中的数的和来表示,
那么答案中一定包含这个数,即我们需要选中这个数。
那么,我们要维护一个背包,维护当前已选中的数能够构成的<=m的和。
时间复杂度nm。
算法2:
我们考虑,由于要从这n个数中选择若干个数,
那么未被选择的数,一定能用被选择的数的和来表示,所以才能被删去。
我们来考虑一种做法:
对于原序列中任意一个a,b(a和b可以相等),
若a+b存在,那么a+b应当被删去。
若a+b<=m,但a+b不存在,那么无解(这是显然的)
我们来证明该做法的正确性。
无解的情况很好判断,以下证明的前提是该序列有解。
那么有解的前提是,对于序列中任意一个a,b,若a+b<=m,那么a+b存在于序列中(这是显然的)
如果对于原序列中一个数s(s<=m),该数能够用序列中某一些数的和来表示,
设s=a0+a1+a2+....+an=a0+(a1+a2+..+an) 。由于有解,a0, a1+a2+..+an也存在于序列中。
那么该数就能够用序列中某两个数的和来表示。
所以判断s是否应当被删去的方法是,判断s是否能表示成a+b,使得a,b存在于序列中。
说了这么多,感觉这个结论还是很显然。
那么关键来了,怎么实现这个算法?
我们需要把原序列中任意两个数的和全部标记起来,然后一一从原序列中删去。
这就是fft模板题了吧。
时间复杂度mlogm
代码:
1 #include <cmath> 2 #include <cstdio> 3 #include <complex> 4 #include <iostream> 5 using namespace std; 6 #define ref(i,x,y)for(int i=x;i<=y;++i) 7 int read() 8 { 9 char c=getchar();int d=0,f=1; 10 for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1; 11 for(;c>='0'&&c<='9';d=d*10+c-48,c=getchar()); 12 return d*f; 13 } 14 const int N=1<<21; 15 const double pi=acos(-1); 16 int rev[N],sz,SZ,n,m,a[N]; 17 bool ans[N]; 18 typedef complex<double> com; 19 class array{ 20 public: 21 com a[N]; 22 void fft(int tp) 23 { 24 ref(i,0,SZ-1)if(rev[i]<i)swap(a[rev[i]],a[i]); 25 for(int i=2;i<=SZ;i<<=1) 26 { 27 int I=i>>1;com w(cos(pi/I),tp*sin(pi/I)); 28 for(int j=0;j<SZ;j+=i) 29 { 30 com W(1,0); 31 for(int k=j;k<j+I;++k,W=W*w) 32 { 33 com A=a[k],B=a[k+I]; 34 a[k]=A+B*W;a[k+I]=A-B*W; 35 } 36 } 37 } 38 if(tp==-1)ref(i,0,SZ-1)a[i]/=SZ; 39 } 40 }A; 41 int main() 42 { 43 n=read(),m=read(); 44 ref(i,1,n)a[i]=read(); 45 while((1<<sz)<m)++sz; 46 ++sz;SZ=1<<sz; 47 ref(i,0,SZ-1)rev[i]=(rev[i>>1]>>1)|((i&1)<<(sz-1)); 48 ref(i,1,n)A.a[a[i]]=com(1,0),ans[a[i]]=1; 49 A.fft(1); 50 ref(i,0,SZ-1)A.a[i]=A.a[i]*A.a[i]; 51 A.fft(-1); 52 ref(i,1,m)if((int)(A.a[i].real()+0.5)>=1) 53 { 54 if(!ans[i]) 55 { 56 cout<<"NO"<<endl; 57 return 0; 58 } 59 ans[i]=0; 60 } 61 cout<<"YES"<<endl; 62 int sum=0; 63 ref(i,1,m)if(ans[i])sum++; 64 printf("%d\n",sum); 65 ref(i,1,m)if(ans[i])printf("%d ",i); 66 printf("\n"); 67 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· Apache Tomcat RCE漏洞复现(CVE-2025-24813)