联考20200801 T1 林海的密码



分析:
很离谱的构造题
第一个点直接输出C条重边就可以了
第二个点增加(k)个点,每个点向终点连2条边
其实第二个点给了我们一些提示,让我们向二进制之类的方向思考问题
考虑构造一个像这样的环

我们算一下方案,其实可以枚举断掉那条边,两端各自沿着红蓝边走向根
答案是(2^0+2^1+2^1+2^2+2^3=17)
我们把环的最后断开,看作一条链,假设有2条边的位置的值为1,否则为0
(A={0,1,0,1,1})
求个前缀和:
(S={0,1,1,2,3})
发现就是上面答案的指数
我们把(C)的二进制写出来,用前缀和的方式构造出一个环,就可以解决了
发现环的大小是(O(2logC))级别的,在某些数据下,无法通过最后一个点
(然而数据很水,可以通过
看看正解:


他的(g)想表达的是,如果选择的方案出现的不连通,在最后一个点接一条到(n)的红边就能够连通的方案数
剩下的推理都没有问题,不过这个随机有点神仙(
(可能这就是人类智慧吧

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

#define maxn 100005

using namespace std;

inline long long getint()
{
	long long num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

long long C;
int n,m;
int a[maxn],cnt,sum;

inline long long gcd(long long p,long long q)
{return q?gcd(q,p%q):p;}
inline long long getrand(long long x,long long p,long long C)
{return ((x*x+p)%C+C)%C;}

int main()
{
	srand(114514);
	C=getint(),n=getint(),m=getint();
	if(C<=100)
	{
		printf("2 %lld
",C);
		while(C--)printf("2 1
");
		return 0;
	}
	while(1)
	{
		long long x=rand();
		do{x=getrand(x,rand(),C);}while(gcd(C,x)>1);
		sum=cnt=0;
		long long f=C,g=x;
		while(1)
		{
			if(g==1){sum+=(a[++cnt]=f-1);break;}
			sum+=(a[++cnt]=f/g),f%=g,g-=f;
			if(cnt+1>n||sum+cnt*2>m)break;
		}
		if(cnt+1>n||sum+cnt*2>m)continue;
		printf("%d %d
",cnt+1,sum+cnt*2);
		for(int i=1;i<=cnt;i++)
		{
			while(a[i]--)printf("%d %d
",cnt+1,i);
			printf("%d %d
",i+1,i);
			printf("%d %d
",i,i+1);
		}
		return 0;
	}
}

原文地址:https://www.cnblogs.com/Darknesses/p/13421659.html