题意:
给出m,n,再给一个m个数的集合让你构造一个序列满足以下的条件:
1.这个序列的所有数都在0-m-1之间(含)
2.这个数列的所有前缀积模(m)都不同
3.所有的前缀积模(m)都不能出现在给你的集合中
4.最大化这个序列的长度
输出任意满足条件的序列。
范围&性质:(1le n le 2 imes 10^5)
分析:
根据题意列出同余方程,我们可以发现当余数从一个点向另一个点转移时,存在(gcd(i,m)|j) ,这样复杂度是(O(n^2))的,需要进行优化,我们发现对于(gcd(i,m)=gcd(j,m))的点对(i,j)他们在图上连的边是相同的,所以我们可以把这样的点的集合看成一个点,整张图就转化成了在DAG上求一个带权最长链,直接DP就好了
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
const int maxn = 2e5+5;
long long n,m;
long long f[maxn],nxt[maxn];
bool vis[maxn];
vector<long long> v[maxn];
long long gcd(long long a,long long b)
{
return b==0?a:gcd(b,a%b);
}
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
long long tmp=exgcd(b,a%b,x,y);
long long d=x;
x=y;
y=d-(a/b)*y;
return tmp;
}
long long cal(long long a,long long b,long long c)
{
long long x,y;
long long d=exgcd(a,b,x,y);
if(c%d) return -1;
x=x*c/d;
b/=d;
x=(x%b+b)%b;
return x;
}
void dfs(long long u)
{
if(f[u]) return ;
long long res=0;
for(int i=u*2;i<m;i+=u)
{
if(v[i].size())
{
dfs(i);
if(f[i]>res)
{
res=f[i];
nxt[u]=i;
}
}
}
f[u]=res+v[u].size();
}
void work()
{
memset(f,0,sizeof(f));
scanf("%lld%lld",&n,&m);
for(long long i=1,x;i<=n;i++)
{
scanf("%lld",&x);
vis[x]=true;
}
for(long long i=1;i<m;i++)
{
if(vis[i]) continue;
long long tmp=gcd(i,m);
v[tmp].push_back(i);
}
dfs(1);
printf("%lld
",f[1]+(!vis[0]));
long long u=1,lst=1;
while(u)
{
for(int i=0;i<v[u].size();i++)
{
long long tmp=v[u][i];
long long ans=cal(lst,m,tmp);
printf("%lld ",ans);
lst=tmp;
}
u=nxt[u];
}
if(!vis[0]) printf("0
");
}
}
int main()
{
zzc::work();
return 0;
}