题目分析:
容易发现将Ci从小到大排序后,若
c1!=c2,直接输出
c1即可
若
c1==c2,那么答案一定小于
c1
容易发现当一个数
x模了一个较小的数后,再模一个大数就没有意义了
于是在这个基础上想到DP,设
fi表示i能否在模的过程中被取到,最后的合法答案就是
fc1−1到
f1中最大的为1的数,显然应该由大到小转移,于是枚举
i从
cn到1
- 若i==ck,那么fi=1,同时给i的倍数打上标记
- 若i是由某个数模后得到的,就需要存在一个j使得fj==1且j−i是前面某个ck的倍数(被标记过)
暴力转移的时间复杂度是
O(nlnn+n2),105的范围无法承受
我们把标记数组记为
g,那么第二种情况
fi=1的条件就是存在
fj==1&&gj−i==1,这样的条件约束是典型的bitset优化(典型到我闻所未闻。。。),等价于
((f>>i) & g).any()==1,any表示存在某一位为1
这样有约束条件、两个取交集的东西常用bitset的&操作优化
听说bitset常用于跑105的
O(n2)算法。。。
所以算法的时间复杂度就是
O(nlnn+wn2),
w听说是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]表示前i个数能否得到j,转移就先继承前面的,再枚举k,
f[i] ∣=f[i−1]>>k∗ci,复杂度。。。似乎有点炸
贴一个
bitset常用函数