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;
}
codeforces801E

 

posted @ 2017-05-25 21:02  gjghfd  阅读(239)  评论(0编辑  收藏  举报