codeforces801E Vulnerable Kerbals
题目大意:
给定n个数,构造一个序列,满足所有前缀积模m互不相等且不与n个数中任意一个相等。最大化序列长度。
将1~m-1每个数作为一个点,如果存在a,使得 i*a=j (mod m),那么从i向j连一条有向边。那么答案就是图中的最长路径。
又因为如果 i*a=j (mod m),则gcd(i,m)|gcd(j,m),所以可以将gcd(i,m)相等的所有点缩成一个点,每个点向gcd(j,m)是gcd(i,m)倍数的点j连权值为i点集大小的边。
显然缩成的每个点是个团,且得到的图是一个DAG图,求一遍拓扑就可以了。
输出序列用拓展欧几里得。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; #define N 200010 #define ll long long vector<int>g[N],a[N]; bool b[N]; ll Last,x,y; int i,j,s[N],k,n,m,c[N],d[N],q[N],l,r,f[N],Ans,p[N]; inline int Max(int x,int y){return x<y?y:x;} inline int Gcd(int x,int y){ if(y==0)return x; return Gcd(y,x%y); } inline void Ex_gcd(ll a,ll b){ if(b==0){x=1;y=0;return;} Ex_gcd(b,a%b); ll t=x;x=y;y=t-(a/b)*y; } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++)scanf("%I64d",&x),b[x]=1; for(i=1;i<m;i++){ if(!b[i])a[Gcd(i,m)].push_back(i); if(m%i==0)for(j=2;j*i<m;j++)g[i].push_back(j*i),d[j*i]++; } for(i=1;i<m;i++)if(m%i==0)s[i]=a[i].size(); for(i=1;i<m;i++) if(m%i==0&&!d[i])q[++r]=i,f[i]=s[i]; while(++l<=r){ x=q[l]; if(f[x]>f[Ans])Ans=x; for(j=0;j<g[x].size();j++){ if(f[x]+s[g[x][j]]>f[g[x][j]])f[g[x][j]]=f[x]+s[g[x][j]],p[g[x][j]]=x; if(--d[g[x][j]]==0)q[++r]=g[x][j]; } } printf("%d\n",f[Ans]+(b[0]?0:1)); for(i=Ans,r=1;i;i=p[i]) for(j=0;j<a[i].size();j++)q[r++]=a[i][j]; if(r>1)printf("%d",q[--r]);Last=q[r]; while(--r){ Ex_gcd(Last,m); x=(x*q[r]/Gcd(Last,m)%m+m)%m; printf(" %I64d",x); Last=Last*x%m; } if(!b[0])puts(" 0"); return 0; }