CF-Global-R-24解题报告

B. Doremy's Perfect Math Class

题意:有一个集合 \(s\),初始有一些元素,每次操作可以选择两个元素并将它们的差加入集合。问若干次操作后集合内元素最多有多少个。

直觉告诉我们,集合中元素的 \(\gcd\) 的倍数都能出现(前提是小于等于最大值)。证明:考虑相邻元素通过若干次减法操作可以得到它们的 \(\gcd\),再与下一个元素进行同样操作,最终得到所有元素的 \(\gcd\)。有了 \(\gcd\) 后,可以将最大值反复减 \(\gcd\) 得到所有元素。

By QAQAutoMaton

namespace run{
	bool main(){
		int n;
		read(n);
		int g=0;
		int m=0;
		for(int i=1;i<=n;++i){
			int x;
			read(x);
			g=__gcd(g,x);
			chkmax(m,x);
		}
		write(m/g,'\n');
		return 0;
	}
}
signed main(){
#ifdef QAQAutoMaton
	freopen("B.in","r",stdin);
	freopen("B.out","w",stdout);
#endif
	int t;
	read(t);
	for(;t;--t)run::main();
	return 0;
}

C. Doremy's City Construction

题意:给你一个数组 \(a\),你需要在元素之间连边,使得没有重边自环且边数尽可能多。要求不存在一组 \(x-y-z\) 使得 \(a_x\le a_y\le a_z\)。输出最多边数。

考虑贪心。首先数组的顺序没有关系,所以可以进行排序,问题转化为不存在左-中-右的一条链。于是可以转化为,每个点要么往左连要么往右连。所以答案一定为分界线左边全部往右连,分界线右边全部往左连。答案即为两边点数的乘积。需要注意的是,左右两边不能有相同元素(否则不满足条件)。枚举分界线取最大值即可。所有元素相同需要特判。

By maroonrk

void slv(){
	int n;cin>>n;
	vi a=readvi(n);
	sort(all(a));
	int ans=n/2;
	rng(i,1,n)if(a[i-1]<a[i])chmax(ans,i*(n-i));
	print(ans);
}

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(0);
	cout<<fixed<<setprecision(20);

	int t;cin>>t;rep(_,t)
	slv();
}

D. Doremy's Pegging Game

题意:有 \(n\) 个钉子,标号 \(1\sim n\),以正 \(n\) 边形的顶点形状排列,正中间有一个较小的钉子。初始时一个橡皮筋绕着这 \(n\) 个顶点,每次可以任意拿走一个钉子(除了中间的特殊钉子),直到橡皮筋碰到中间的钉子时停止。特殊地,在 \(n\) 为偶数时正对面的两个钉子缠绕不会碰到中间的钉子(因为较小)。求拿走钉子的方案数(考虑顺序)。

可以考虑最终状态。容易发现,最终状态一定是一些钉子聚集在某一半,而另一边的钉子全部删空了。此时聚集的这一半只与两端的钉子位置有关,中间的钉子任何状态都有可能。于是可以枚举这两段的钉子的距离 \(i\),考虑剩下的如何填。首先有一个想法,剩下的任意顺序取都可以,但这是不对的。由于这是最终状态,而一旦碰到中间钉子就会停止,所以要求最终状态的上一步还没有碰到中间钉子。所以最后一步其实是有限制的,而限制的区间则与两段钉子的距离相等。

实现上,枚举距离 \(i\),再枚举最终状态下除了端点还剩的钉子个数 \(j\),则答案为 \(i\times\binom{i-1}{j}\times (n-3-j)!\)。由于聚集的那一半位置可以仁义移动,还要再乘 \(n\)

By QAQAutoMaton

namespace run{
	Z fac[5005],inv[5005],invf[5005];
	Z C(int a,int b){return fac[a]*invf[b]*invf[a-b];}
	bool main(){
		int n;
		read(n,p);
		fac[0]=fac[1]=inv[1]=invf[0]=invf[1]=1;
		for(int i=2;i<=n;++i){
			fac[i]=fac[i-1]*i;
			inv[i]=(p-p/i)*inv[p%i];
			invf[i]=inv[i]*invf[i-1];
		}
		Z ans(0);
		for(int i=1;i<=n-(n>>1);++i){
			Z c=0;
			for(int l=i+1;l<=n;++l)if(l-i-1<(n>>1) && (n-l)<(n>>1)){
				++c;
			}
			c*=n;
			Z v=0;
			if(i==1)v=fac[n-2];
			else{
				for(int j=0;j<=i-2;++j)v+=C(i-2,j)*fac[n-3-j];
			}
			ans+=v*c;
		}
		write(ans.x,'\n');
		return 0;
	}
}

E. Doremy's Number Line

题意:有一个整数数轴,初始每个点都没有颜色。你有 \(n\) 种操作,第 \(i\) 种操作有两个属性 \(a_i,b_i\)。进行操作时,你可以选择一个没涂色的位置 \(x\) 涂上颜色 \(i\),要求:要么 \(x\le a_i\),要么存在一个涂过色的 \(y\le a_i\)\(x\le y+b_i\)。你可以以任意顺序执行这些操作,但每种最多一次,问是否能将 \(k\) 染成颜色 \(1\)

首先有一个显然的推论:操作顺序固定时,设最后一次染的位置的最大值为 \(x\),则一定可以通过微调来染到 \(\le x\) 的任意位置。

可以倒序考虑。要想将 \(k\) 染成颜色 \(1\),则最后一次操作一定为 \(1\) 操作。若 \(k\le a_1\) 则可以直接染,否则必须有“跳板 \(x\)”。容易发现,\(x\) 必须 \(\ge k-b_1\)。由于如果能取到更大,则一定也能取到最小值,所以可以要求 \(x=k-b_1\)。问题转化为是否通过 \(2\sim n\) 操作来涂上 \(x\)

考虑哪些操作可能覆盖到 \(x\):要么 \(a_i\ge y\),要么 \(a_i+b_i\ge y\)(后者包含前者)。如果有满足前者要求的操作,则显然有解;如果有大于等于两个满足后者要求的操作也有解。证明:设该两个操作分别为 \(i,j\)\(a_i\le a_j\),则可以先通过 \(j\) 操作涂上 \(a_i\) 位置,再通过 \(i\) 操作涂上 \(a_i+b_i\) 位置。如果没有满足后者要求的操作则无解。

现在只剩一种可能的情况:有且仅有一种满足后者要求的操作。此时可以直接使用该操作,即让 \(x\leftarrow x-b_i\)。每一步均如此递归处理即可。

posted @ 2023-03-01 16:06  曹轩鸣  阅读(35)  评论(0编辑  收藏  举报