标签:style color os io for ar 问题 log line
题意:
给出n个整数,对每个整数可以减去0-k的任意一个数
求这样操作后,n个数的最大GCD是多少
分析:
我们首先可以知道n个整数中最小的数是多少
而且,最终的答案肯定不大于这个数
这个n个整数中最小的数是答案的上限
然后对于答案的下限
可以肯定的是
1肯定是答案的下限
2呢?3呢?为什么1一定是
其实,0-k+1,都可以作为答案
为什么?
可以把k想象成一个剪刀
对k+1来说,任何数都可以剪掉0-k变成k+1的倍数(任何数模k+1的结果都是0-k)
所以0-k也可以,综上0-k+1都可以,所以答案的下限是k+1
答案处于[k+1,minn]
如果minn<k+1,那么就是minn
如果minn>k+1,则我们枚举这其中所有的数
看最大的能使得所有数列中的数经过操作之后GCD(all)确实能等于这个数的数是多少
也就是验证这个数的可行性,找出可行的数中最大的那个
怎么验证呢?给出一个数m,怎么才能知道可行不可行呢?
一步步来推理
每个数模m,结果都是0--m-1
对于0--m-1,我们的剪刀最多只能剪掉k
所以如果数列中某个数模m的结果大于k,则m不可行
那么我们是不是能这样做
对数列中的每一个数都模m呢
估计一下复杂度,枚举是100W,最多有30万个数,则100w*30w,T!
转化一下这个问题
我们是对每个数尝试一下它是不是处于可行区间
反过来,如果可行区间有这个数的话,是一样的
如果所有可行区间中存在的数的和是n,则这个m是可行的
可行区间是什么?
一个数模m即是x =m*d+x%m
x%m小于等于k,也就是x处于[m*d,m*d+k]
那么所有的可行区间就是
[m*1,m*1+k],[m*2,m*2+k],[m*3,m*3+k],[m*4,m*4+k]...
有logn个这样的区间
算下复杂度,n*logn
这些区间内出现的数的总和我们可以累加前缀和之差得到
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> using namespace std; const int NN=555555; int vistmp[NN*2]; int vis[NN*2]; int f[NN]; int n,k; int maxn; bool check(int s){ int sum=0; for(int i=1;i*s<=maxn;i++){ sum += vis[min(maxn,i*s+k)] - vis[i*s-1];//注意细节 if(s==7){ } } return sum==n; } int main(){ #ifndef ONLINE_JUDGE freopen("G:/in.txt","r",stdin); //freopen("G:/myout.txt","w",stdout); #endif cin>>n>>k; int minn=1<<30; for(int i=1;i<=n;i++){ cin>>f[i]; minn=min(minn,f[i]); maxn=max(maxn,f[i]); vistmp[f[i]]++; } vis[minn]=vistmp[minn]; for(int i=minn+1;i<=maxn;i++) vis[i]=vis[i-1]+vistmp[i]; if(minn<=k+1){ cout<<minn<<endl; return 0; } for(int i=minn;i>=k+1;i--){ if(check(i)){ cout<<i<<endl; return 0; } } }
Codeforces 354C Vasya and Beautiful Arrays[dp+暴力]
标签:style color os io for ar 问题 log line
原文地址:http://blog.csdn.net/u011775691/article/details/38819265