说明
题目链接
就做出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=(2n−1−1)+(2n−1−1)+1=n−2
②对于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}
n−i−2≤n−2n−1−2=2n−3<2n−1,因此最大值就是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
2t−i=n−1−i≥1,故此式一定成立。
②对于剩下的
(
i
−
1
)
+
j
(i - 1) + j
(i−1)+j 个要选的红钉,顺序无所谓,故乘以
(
i
−
1
)
+
j
(i - 1) + j
(i−1)+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)=n∑i=tn−2∑j=0n−i−2(jn−i−2)⋅(2t−i)⋅(i+j−1)!
二、再考虑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=(2n−1)+(2n−1)+1=n−1
②对于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}
n−i−2≤n−2n−2=2n−4<2n,因此最大值就是
n
−
i
−
2
n-i-2
n−i−2。
2.现在考虑一下最后取的那个红钉有多少种选法。
①对于每种连续的 i 个红钉,左右两侧各自的 i - t 个红钉不能最后选择,否则一定在选这些红钉之前,就选够了连续的 t 个红钉。则对于每种连续的 i 个红钉,最后取的那个红钉有 i - 2 * ( i - t ) = 2 * t - i 种选法。
说明:
2
t
−
i
=
n
−
i
≥
1
2t-i=n-i≥1
2t−i=n−i≥1,故此式一定成立。
②对于剩下的
(
i
−
1
)
+
j
(i - 1) + j
(i−1)+j 个要选的红钉,顺序无所谓,故乘以
(
i
−
1
)
+
j
(i - 1) + j
(i−1)+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)=n∑i=tn−1∑j=0n−i−2(jn−i−2)⋅(2t−i)⋅(i+j−1)!
三、综合两种情况
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=tn−2∑j=0n−i−2(jn−i−2)⋅(2t−i)⋅(i+j−1)!)+[n=even](n⋅(n−2)!)
其中
(
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!)
(jn−i−2)=(n−i−2−j)!j!(n−i−2)!=(n−i−2)!⋅inv((n−i−2−j)!)⋅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;
}
总结
太菜了,为什么米娜桑天生会二分图最大匹配~