Codeforces Global Round 24 A~D 题解

说明

题目链接
就做出AB题,C题想了一个半小时。和我同场竞技的两位选手都做出了C,还是我太菜了555~

A - Doremy’s Paint

答案等价于求包含不同元素个数最大的区间,就是全区间。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e5+5;
int a[maxn];
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--){
		int n;
		cin>>n;
		for(int i=1;i<=n;i++)cin>>a[i];
		cout<<1<<" "<<n<<endl;
	}
	return 0;
}

B - Doremy’s Perfect Math Class

两个数不断相减,本质就是不断取模,参考更相减损术求最大公约数的原理。因此,如果没法继续减损,也就是说一直减损到两个数相等,这个数就是最大公约数。求这n个数的最大公约数,也就是求这n个数能够减损出的最小的数。那么以这个最小数为单元,用n个数中的最大数不断减这个最小数,所求就是最大数除以最大公约数。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int a[maxn];
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--){
		int n;cin>>n;
		for(int i=1;i<=n;i++)cin>>a[i];
		int fac=a[1];
		for(int i=2;i<=n;i++){
			fac=__gcd(fac,a[i]);
		}
		int ans=a[n]/fac;
		cout<<ans<<endl;
	}
	return 0;
}

C - Doremy’s City Construction

本质是二分图最大匹配。赛中想了一个半小时没思路,赛后后来问了学长才搞懂。就是先排序,就假设是从小到大排,那么将此图二分,小的节点在左,大的节点在右,只需要找二分的分界线。因为将此图左右全连接,一定对于每个三元组都满足大-小-大。因此遍历二分分界线,答案是每一条分界线对应的最大匹配。但是由于左右的节点值不能相同,因此在划定分界线时需要跳过相同节点间的分界线。

直接用学长的图说明吧:
二分图最大匹配

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e5+5;
int a[maxn];
map<int,int> mp;
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--){
		mp.clear();
		int n;cin>>n;
		for(int i=1;i<=n;i++)cin>>a[i];
		sort(a+1,a+1+n);
		for(int i=1;i<=n;i++){
			mp[a[i]]++;
		}
		int num=mp.size();
		int ans=n/2;
		int cur=0;
		for(auto x:mp){
			cur+=x.second;
			ans=max(ans,cur*(n-cur));
		}
		cout<<ans<<endl;
	}
	return 0;
}

D - Doremy’s Pegging Game

题目翻译

哆来咪有n+1个螺钉。有n个红色螺钉排列成一个正n边形的顶点,按逆时针方向从1到n编号。在多边形的中间还有一个直径比红色螺钉小的蓝色螺钉。一根橡皮筋环绕在红色螺钉上。

哆来咪今天很无聊,决定玩一个游戏。最初,她有一个空数组a。当橡皮筋没有碰到蓝色螺钉时,她会:

①选择还没有被移除的红色螺钉i;
②移除红色螺钉i;
③在a后面加上i。

哆来咪想知道通过上面的过程可以产生多少种不同的数组a。既然答案可以很大,只要求你模p输出,p保证是质数。

思路

操作结束的状态是蓝钉不在剩余红钉所组成的多边形内,通过自己尝试,易知取到连续的⌊n/2⌋(n/2的下取整)个红钉时结束。设 t = ⌊ n 2 ⌋ t=⌊\frac{n}{2}⌋ t=2n

一、先考虑n为奇数(n≥3)的情况
1.结束时的状态可以看作是取了 i (t <= i <= n - 2)个连续个红钉和 j (0 <= j <= n - 2 - i)个不与之相邻的红钉。
说明:
①i的最小值就是t,最大值是先分别取两段t-1,最后取两段中间的螺钉,令这两段连结。故:
i m a x = ( n − 1 2 − 1 ) + ( n − 1 2 − 1 ) + 1 = n − 2 i_{max}=(\frac{n-1}{2}-1)+(\frac{n-1}{2}-1)+1=n-2 imax=(2n11)+(2n11)+1=n2
②对于j,j的最小值就是0,最大值可以这么求:取了i个连续红钉后,剩余n-i个红钉,最大连续段两侧至少要分别隔开一个,因此剩余n-i-2个红钉可选为不与之相邻红钉,且 n − i − 2 ≤ n − n − 1 2 − 2 = n − 3 2 < n − 1 2 n-i-2≤n-\frac{n-1}{2}-2=\frac{n-3}{2}<\frac{n-1}{2} ni2n2n12=2n3<2n1,因此最大值就是n-i-2。

2.现在考虑一下最后取的那个红钉有多少种选法。
①对于每种连续的 i 个红钉,左右两侧各自的 i - t 个红钉不能最后选择,否则一定在选这些红钉之前,就选够了连续的 t 个红钉。则对于每种连续的 i 个红钉,最后取的那个红钉有 i - 2 * ( i - t ) = 2 * t - i 种选法。
说明: 2 t − i = n − 1 − i ≥ 1 2t-i=n-1-i≥1 2ti=n1i1,故此式一定成立。

②对于剩下的 ( i − 1 ) + j (i - 1) + j (i1)+j 个要选的红钉,顺序无所谓,故乘以 ( i − 1 ) + j (i - 1) + j (i1)+j的阶乘,即这些红钉的全排列。
说明:无论按什么顺序取这些红钉,都不可能满足取连续t个红钉的终止条件,因为最后取的那次才终止。

3.以上只是对于既定位置的情况,求排列组合。
①对于n边形,显然有n种不同的选择连续 i 个红钉的选法。因为这个i长序列的首部位置有n种可能。
②选定i长序列的位置后,由上述分析,0 <= j <= n - 2 - i,故其余j个与之不相邻红钉就是在 n - 2 - i 个位置中分别选0、1、……、n - 2 - i个位置放红钉。

4.得出n为奇数时的公式
a n s ( n = o d d ) = n ∑ i = t n − 2 ∑ j = 0 n − i − 2 ( n − i − 2 j ) ⋅ ( 2 t − i ) ⋅ ( i + j − 1 ) ! ans(n=odd)=n\sum_{i=t}^{n-2}\sum_{j=0}^{n-i-2}\dbinom{n-i-2}{j}·(2t-i)·(i+j-1)! ans(n=odd)=ni=tn2j=0ni2(jni2)(2ti)(i+j1)!

二、再考虑n为偶数(n≥4)的情况
1.结束时的状态可以看作是取了 i (t <= i <= n - 1)个连续个红钉和 j (0 <= j <= n - 2 - i)个不与之相邻的红钉。
说明:
①i的最小值就是t,最大值是先分别取两段t-1,最后取两段中间的螺钉,令这两段连结。故:
i m a x = ( n 2 − 1 ) + ( n 2 − 1 ) + 1 = n − 1 i_{max}=(\frac{n}{2}-1)+(\frac{n}{2}-1)+1=n-1 imax=(2n1)+(2n1)+1=n1
②对于j,j的最小值就是0,最大值可以这么求:取了i个连续红钉后,剩余n-i个红钉,最大连续段两侧至少要分别隔开一个,因此剩余n-i-2个红钉可选为不与之相邻红钉,且 n − i − 2 ≤ n − n 2 − 2 = n − 4 2 < n 2 n-i-2≤n-\frac{n}{2}-2=\frac{n-4}{2}<\frac{n}{2} ni2n2n2=2n4<2n,因此最大值就是 n − i − 2 n-i-2 ni2

2.现在考虑一下最后取的那个红钉有多少种选法。
①对于每种连续的 i 个红钉,左右两侧各自的 i - t 个红钉不能最后选择,否则一定在选这些红钉之前,就选够了连续的 t 个红钉。则对于每种连续的 i 个红钉,最后取的那个红钉有 i - 2 * ( i - t ) = 2 * t - i 种选法。
说明: 2 t − i = n − i ≥ 1 2t-i=n-i≥1 2ti=ni1,故此式一定成立。

②对于剩下的 ( i − 1 ) + j (i - 1) + j (i1)+j 个要选的红钉,顺序无所谓,故乘以 ( i − 1 ) + j (i - 1) + j (i1)+j的阶乘,即这些红钉的全排列。
说明:无论按什么顺序取这些红钉,都不可能满足取连续t个红钉的终止条件,因为最后取的那次才终止。

3.以上只是对于既定位置的情况,求排列组合。
①对于n边形,显然有n种不同的选择连续 i 个红钉的选法。因为这个i长序列的首部位置有n种可能。
②选定i长序列的位置后,由上述分析,0 <= j <= n - 2 - i,故其余j个与之不相邻红钉就是在 n - 2 - i 个位置中分别选0、1、……、n - 2 - i个位置放红钉。

4.得出n为偶数时的公式
a n s ( n = e v e n ) = n ∑ i = t n − 1 ∑ j = 0 n − i − 2 ( n − i − 2 j ) ⋅ ( 2 t − i ) ⋅ ( i + j − 1 ) ! ans(n=even)=n\sum_{i=t}^{n-1}\sum_{j=0}^{n-i-2}\dbinom{n-i-2}{j}·(2t-i)·(i+j-1)! ans(n=even)=ni=tn1j=0ni2(jni2)(2ti)(i+j1)!

三、综合两种情况
a n s ( n ) = n ( ∑ i = t n − 2 ∑ j = 0 n − i − 2 ( n − i − 2 j ) ⋅ ( 2 t − i ) ⋅ ( i + j − 1 ) ! ) + [ n = e v e n ] ( n ⋅ ( n − 2 ) ! ) ans(n)=n(\sum_{i=t}^{n-2}\sum_{j=0}^{n-i-2}\dbinom{n-i-2}{j}·(2t-i)·(i+j-1)!)+[n=even](n·(n-2)!) ans(n)=n(i=tn2j=0ni2(jni2)(2ti)(i+j1)!)+[n=even](n(n2)!)
其中 ( n − i − 2 j ) = ( n − i − 2 ) ! ( n − i − 2 − j ) ! j ! = ( n − i − 2 ) ! ⋅ i n v ( ( n − i − 2 − j ) ! ) ⋅ i n v ( j ! ) \dbinom{n-i-2}{j}=\frac{(n-i-2)!}{(n-i-2-j)!j!}=(n-i-2)!·inv((n-i-2-j)!)·inv(j!) (jni2)=(ni2j)!j!(ni2)!=(ni2)!inv((ni2j)!)inv(j!)

代码

①注意凡是出现三个数乘积的情况一定要两个数乘完立即取模,再将此结果乘第三个数再取模,而不能只取一次模。取模和乘法优先级相同,从左到右运算。
②用费马小定理加快速幂求5000个逆元会超时,因为求n的逆元复杂度是O(logn),那么二重循环内总复杂度O(n^2logn),刚好超时。优化:先用费马小定理+快速幂求n!的逆元,然后递推出(n-1)!~2!的逆元,0!和1!的逆元就是1。
递推:在模p意义下,有:
( n + 1 ) ! = n ! ⋅ ( n + 1 ) (n+1)!=n!·(n+1) (n+1)!=n!(n+1)
1 n ! = 1 ( n + 1 ) ! ⋅ ( n + 1 ) \frac{1}{n!}=\frac{1}{(n+1)!}·(n+1) n!1=(n+1)!1(n+1)
i n v [ n ! ] = i n v [ ( n + 1 ) ! ] ⋅ ( n + 1 ) inv[n!]=inv[(n+1)!]·(n+1) inv[n!]=inv[(n+1)!](n+1)
i n f a c [ i ] = i n f a c [ i + 1 ] ⋅ ( i + 1 ) infac[i]=infac[i+1]·(i+1) infac[i]=infac[i+1](i+1)
补题的时候,Re了1发,TLE了2发,WA了3发才AC,属实难崩……

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 5010;
int fac[maxn],infac[maxn];
int ksm(int a, int b, int mod){
	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b=b>>1;
	}
	return ans;
}
void get_infac(int mod, int n){
	infac[n]=ksm(fac[n],mod-2,mod);
	infac[0]=infac[1]=1;
	for(int i=n-1;i>=2;i--){
		infac[i]=infac[i+1]*(i+1)%mod;
	}
}
void get_fac(int mod, int n){
	fac[0]=fac[1]=1;
	for(int i=2;i<=n;i++){
		fac[i]=fac[i-1]*i%mod;
	}
}
int C(int i, int j, int mod){
	return ((fac[i]*infac[i-j])%mod)*infac[j]%mod;
}
signed main()
{
	int n,p;
	cin>>n>>p;
	get_fac(p,n);
	get_infac(p,n);
	int t=n/2;
	int ans=0;
	for(int i=t;i<=n-2;i++){
		for(int j=0;j<=n-i-2;j++){
			int cur=((C(n-i-2,j,p)*((2*t+p-i)%p))%p)*fac[i+j-1]%p;
			ans=(ans%p+cur%p)%p;
		}
	}
	if(n%2==0)ans=(ans%p+fac[n-2]%p)%p;
	ans=ans*n%p;
	cout<<ans<<endl;
	return 0;
}

总结

太菜了,为什么米娜桑天生会二分图最大匹配~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

keguaiguai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值