CF800 C

来源:互联网 发布:matlab生成随机数矩阵 编辑:程序博客网 时间:2024/06/02 05:06

题意:
在模m的意义下,ban掉n个数。构造一个最长的数列,使得:
1、前缀之积两两不等
2、前缀之积不能出现n个被ban的值
n< m<=200000

#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>#include<iostream>#include<vector>#define N 410000#define pb push_backusing namespace std;struct node{int x,y,nex;}a[20*N];int n,m,len,fir[N],f[N],g[N],sta[N],belong[N],tp,dfn[N],low[N],id,cnt,du[N],e[N*20][2];int p[N*20],tail,ans,w,A[N],res[N],num,fr[N];bool b[N],insta[N];vector<int> v[N];void ins(int x,int y){    a[++len].x=x;a[len].y=y;a[len].nex=fir[x];fir[x]=len;}int gcd(int a,int b){    if(b==0) return a;    return gcd(b,a%b);}void tarjan(int x,int fa){    dfn[x]=low[x]=++id;    sta[++tp]=x;insta[x]=1;    for(int k=fir[x];k;k=a[k].nex)    {        int y=a[k].y;        if(dfn[y]==0) {tarjan(y,x);low[x]=min(low[x],low[y]);}        else if(insta[y]) low[x]=min(low[x],dfn[y]);    }    if(low[x]>dfn[fa])    {        cnt++;        int y;        do{            y=sta[tp];sta[tp--]=0;            insta[y]=0;            belong[y]=cnt;            g[cnt]+=b[y];            v[cnt].pb(y);        }while(y!=x);        int o=1;    }}void tpsort(){    for(int i=1;i<=cnt;i++) if(du[i]==0) f[i]=g[i],p[++tail]=i;    for(int i=1;i<=tail;i++)    {        int x=p[i];        if(f[x]>ans) {ans=f[x];w=x;}        for(int k=fir[x];k;k=a[k].nex)        {            int y=a[k].y;            du[y]--;            if(f[x]+g[y]>f[y]) {f[y]=f[x]+g[y];fr[y]=x;}            if(du[y]==0) p[++tail]=y;        }    }}int exgcd(int a,int b,int &x,int &y){    if(b==0) {x=1;y=0;return a;}    int t=a/b,xx,yy,g;    g=exgcd(b,a%b,xx,yy);    x=yy;y=xx-t*yy;    return g;}void make_ans(){    int x=w;    while(x)    {        int siz=v[x].size();        for(int i=0;i<siz;i++) if(b[v[x][i]]) A[++num]=v[x][i];        x=fr[x];    }    res[1]=A[1];    for(int i=2;i<=num;i++)    {        int x,y,g;g=exgcd(A[i-1],m,x,y);        x=(x%m+m)%m;        x=1ll*x*(A[i]/g)%m;        res[i]=x;    }    if(b[0]) res[++num]=0;    printf("%d\n",num);    for(int i=1;i<=num;i++) printf("%d ",res[i]);}int main(){    scanf("%d%d",&n,&m);    for(int i=0;i<m;i++) b[i]=1;    for(int i=1;i<=n;i++)    {        int x;scanf("%d",&x);        b[x]=0;    }    for(int i=1;i<m;i++)    {        int g=gcd(i,m);        ins(i,g+m);    }    for(int i=1;i<m;i++)        for(int j=i;j<m;j+=i)            ins(i+m,j);    for(int i=1;i<m;i++) if(dfn[i]==0) tarjan(1,0);    for(int i=1;i<=len;i++) e[i][0]=a[i].x,e[i][1]=a[i].y;    for(int i=1;i<=2*m;i++) fir[i]=0;    int tmp=len;len=0;    for(int i=1;i<=tmp;i++)     {        int x=belong[e[i][0]],y=belong[e[i][1]];        if(x!=y) {ins(y,x);du[x]++;}    }    tpsort();    make_ans();    return 0;}

题解:
直接做一个前缀积的数列,变回原数列是容易的
在能一次转移到的数字间连边,缩点后拓补图dp一下就可以了
直接连边是O(m2)
考虑xai=ai+1(%m)有解的条件是gcd(ai,m)|ai+1
那么对于一个数字i,连上i>gcd(i,m)d>i(d|i)这些边就可以了
复杂度O(mlogm)
现场做的时候一直在想原根,按二的幂分组什么的。。先入为主了,实在不应该

0 0