雅礼集训 Day10 T1 数字重排 【bitset优化DP】

2019-04-14 09:08发布

在这里插入图片描述

题目分析:

容易发现将Ci从小到大排序后,若c1!=c2c_1!=c_2,直接输出c1c_1即可
c1==c2c_1==c_2,那么答案一定小于c1c_1
容易发现当一个数xx模了一个较小的数后,再模一个大数就没有意义了 于是在这个基础上想到DP,设fif_i表示i能否在模的过程中被取到,最后的合法答案就是fc11f_{c_1-1}f1f_1中最大的为1的数,显然应该由大到小转移,于是枚举iicnc_n到1
  • i==cki==c_k,那么fi=1f_i=1,同时给i的倍数打上标记
  • ii是由某个数模后得到的,就需要存在一个jj使得fj==1f_j==1jij-i是前面某个ckc_k的倍数(被标记过)
暴力转移的时间复杂度是O(nlnn+n2)O(nln n+n^2),105的范围无法承受
我们把标记数组记为gg,那么第二种情况fi=1f_i=1的条件就是存在fj==1&&gji==1f_j==1&&g_{j-i}==1,这样的条件约束是典型的bitset优化(典型到我闻所未闻。。。),等价于((f>>i) & g).any()==1((f>>i) ~&~g).any()==1,any表示存在某一位为1
这样有约束条件、两个取交集的东西常用bitset的&操作优化 听说bitset常用于跑105的O(n2)O(n^2)算法。。。
所以算法的时间复杂度就是O(nlnn+n2w)O(nln n+frac {n^2}w)ww听说是32… 代码巨短: #include #include #include #define maxn 100005 using namespace std; int n,a[maxn]; bitset<maxn>f,g; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n); if(a[1]!=a[2]) printf("%d",a[1]),exit(0); n=unique(a+1,a+1+n)-a-1; f[0]=1; for(int i=a[n],j=n;i>=1;i--) if(a[j]==i){ f[i]=1; for(int k=j;k<=a[n];k+=j) g[k]=1; j--; } else if(((f>>i)&g).any()) f[i]=1; for(int i=a[1]-1;i>=0;i--) if(f[i]) printf("%d",i),exit(0); } PS:也可以让C由大到小排序,令f[i][j]f[i][j]表示前i个数能否得到j,转移就先继承前面的,再枚举k,f[i] =f[i1]>>kcif[i]~|=f[i-1]>>k*c_i,复杂度。。。似乎有点炸 贴一个bitset常用函数