T1:math
题目链接:
http://zhengruioi.com/contest/156/problem/471
题解:
先讲讲我的乱搞做法。对于前面70%,我跑了背包。因为背包有后效性...我做了两次,也就是迭代了一下
剩下的30%随机化了一波。就是先把每个数的20以内的倍数暴力的算出来对k取模然后丢到一个大小为k的桶里面去。因为题目就是让你给每个数一个系数,于是我就每次随机两个位置相加判断在模k的意义下是否出现过,如果没有出现过就加入答案中,咳咳重复1e7次即可A掉本题
下面说说题解做法:
$ax+by=z$存在整数解,当且仅当$gcd(a, b)∣z$。
那么,若z可以被凑出,即 $\sum_{i=1}^{n} x_ia_i = z$,当且仅当 $gcd(a_1, a_2,⋯, a_n)∣z$。
因此,答案只能是gcd的整数倍。
但是,这样考虑x有可能是负数,但是在mod k的条件下,我们可以把x调为非负整数。
时间复杂度$O((n + k)logv)$。
乱搞代码
#include<algorithm> #include<cstring> #include<cstdio> #include<iostream> #include<vector> #include<time.h> using namespace std; const int N=1e6+15; int n,k; int a[N],f[N]; inline int read(){ char ch=getchar();int s=0,f=1; while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return s*f; } namespace task1 { void main() { for (int i=1;i<=n;i++) for (int j=0;!f[1ll*j*a[i]%k];j++) f[1ll*j*a[i]%k]=1; for (int i=1;i<=n;i++) { for (int j=0;j<=k;j++) f[j]|=f[((j-a[i])%k+k)%k]; for (int j=0;j<=k;j++) f[j]|=f[((j-a[i])%k+k)%k]; } int s=0; for (int j=0;j<k;j++) s+=f[j]; printf("%d\n",s); for (int j=0;j<k;j++) if (f[j]) printf("%d ",j); } } int gcd(int a,int b) {if (!b) return a;else return gcd(b,a%b);} namespace task2 { void main() { printf("%d\n",k); for (int i=0;i<k;i++) printf("%d ",i); } } int main() { n=read();k=read(); for (int i=1;i<=n;i++) a[i]=read()%k; for (int i=1;i<=n;i++) if (a[i]==1||gcd(a[i],k)==1) {task2::main();return 0;} if (n<=1000) {task1::main();return 0;} srand(time(0)); vector <int> p; for (int i=1;i<=n;i++) { for (int j=0;j<=20;j++) { int q=1ll*j*a[i]%k; if (!f[q]) { p.push_back(q); f[q]=1; } } } for (int i=1;i<=1e7;i++) { int si=p.size(); int l=rand()%si,r=rand()%si; if (!f[(p[l]+p[r])%k]) { p.push_back((p[l]+p[r])%k); f[(p[l]+p[r])%k]=1; } } int s=0; for (int j=0;j<k;j++) s+=f[j]; printf("%d\n",s); for (int j=0;j<k;j++) if (f[j]) printf("%d ",j); return 0; }