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\)。每一步均如此递归处理即可。