题意:
在模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_back
using 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)
现场做的时候一直在想原根,按二的幂分组什么的。。先入为主了,实在不应该