CF772C Vulnerable Kerbals 图论+数论gcd

题意:

给出m,n,再给一个m个数的集合让你构造一个序列满足以下的条件:

1.这个序列的所有数都在0-m-1之间(含)

2.这个数列的所有前缀积模\(m\)都不同

3.所有的前缀积模\(m\)都不能出现在给你的集合中

4.最大化这个序列的长度

输出任意满足条件的序列。

范围&性质:\(1\le n \le 2\times 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\n",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\n");
	}
	
}

int main()
{
	zzc::work();
	return 0;
}
posted @ 2020-09-19 08:17  youth518  阅读(179)  评论(0编辑  收藏  举报