Codeforces Round #879 Div.2
前言:VP了一把,rk731,如果赛上有这发挥就好了。
果然,D是分水岭,一直都是。
Unit Array
题面:
给定一个长度为
; 。
你可以对序列进行若干次修改,每次修改可以把序列中的
给定一个序列,问最少需要几次修改使它变成一个好的序列。
题解:显然,我们只需要将负数改为正数。设有
满足第一个条件,需要代价
Maximum Strength
题面:
每一种材料的力量由一个十进制整数表示。
对于一个武器,由两种材料构成。假如第一种材料的力量为
如果两个数字长度不一样,则要将短的数字用前导零补成相同长度。
现在,你拥有无数个力量在
题解:
乍一看,毫无思路,但我们显然是尽量让每一位的差变小才对。
对于最高的已经确定的几位,我们直接跳过,然后找到第一个
此时,注意到我们可以划分两个数的上下界,然后下一位令较大数为0,较小数为9,此后的所有位全部可以用0,9两个数交替了。
所以答案为:
Game with Reversing
题面:
小 L 和小 S 在玩游戏。他们有两个长度均为
小 L 的回合,他可以选择
小 S 的回合,她可以选择
如果两个串
题解:千年难遇的水C题。
注意到,小L可以修改任意字符,他有两个选择:在不翻转的状态下相同,翻转后相同。
这里我们分别计算出
对于第一种情况,小 L 至少需要
Survey in Class
题面:
有
现在你要问一些不重复的问题,最大化学生分数的极差,并输出这个值。
有意思的问题,不过比较简单。
首先,我们将其放在数轴上,视作线段,则对于选了
所以,
问题化为:在数轴上选若干个点,使得点最多的线段减去点最少的线段的差最大。
显然,必定存在一种最优方案,点最少的线段里没有点。
证明:当两线段不交时,显然。
当两线段相交时,必定是相交部分的点,而这个点会被做差抵消掉,故无用。
换句话说,我们要求:
其中
处理
- 不相交:这显然是最好
左侧与 相交。这样的话,相交线段的右端点自然是越小越好,我们记录所有线段里右端点最小的数,记作 右侧与 相交,类比上一个,相交线段的左端点自然是越大越好,我们记录所有线段里左端点最小的数,记作 包含 。这种情况可以直接被处理掉。
我们直接处理最大线段长
和 最小线段长 ,这种情况下答案绝不会大于 ,可以直接将其计入答案。因为如果最大线段和最小线段是前三种情况,答案会被更新掉
那么,做法显然:
int main(){
ios::sync_with_stdio(false);
read(t);
while(t--){
read(n);read(m);
for(int i=1;i<=n;i++)read(a[i].l),read(a[i].r);
int mx=0,mn=0x3f3f3f3f,L=-1,R=0x3f3f3f3f;
for(int i=1;i<=n;i++){
R=min(R,a[i].r),L=max(L,a[i].l);
mx=max(mx,a[i].r-a[i].l+1);
mn=min(mn,a[i].r-a[i].l+1);
}
int ans=mx-mn;
for(int i=1;i<=n;i++){
ans=max(ans,a[i].r-a[i].l+1-min(max(0,a[i].r-L+1),max(0,R-a[i].l+1)));
}
ans<<=1;
cout<<ans<<"\n";
}
}
MEX of LCM
题面:
给定一个长度为
题面一句话,思想两个字:暴力。
注意到,第
对于求解存在性的答案,可以通过估计答案上界的办法减少复杂度,mex问题是一个典型。
那么,我们只需要求出所有不大于
这里我们记
我们暴力自后向前转移即可。时间复杂度为什么是对的?因为由
而注意到求
但真的没有优化空间了吗?
我们依次合并
由此,我们从
注意空间可能有点卡,这里可以滚掉。
int t,a[N],n,f[2][105],g[N<<1],d[2],tot,vis[N<<1];
const int lim=4300000;
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
signed main(){
read(t);
while(t--){
read(n);tot=d[0]=d[1]=0;
for(int i=1;i<=n;i++)read(a[i]);
for(int i=n;i;i--){
int x=i&1,y=a[i];d[x]=0;
for(int j=1;j<=d[x^1];++j){
ll k=1ll*a[i]*f[x^1][j]/(y=gcd(y,f[x^1][j]));
if(k<=lim&&k!=f[x][d[x]]){
f[x][++d[x]]=k;g[++tot]=k;
}
}
if(f[x][d[x]]!=a[i]&&a[i]<=lim)f[x][++d[x]]=g[++tot]=a[i];
}
for(int i=1;i<=tot;i++)vis[g[i]]=1;
for(int i=1;i<=lim;i++)if(!vis[i]){
print(i);puts("");break;
}
for(int i=1;i<=tot;i++)vis[g[i]]=0;
}
}
Typewriter
题面:
最近,Polycarp收到了一台特殊的打字机作为礼物!不幸的是,这台打字机有一个相当奇怪的设计。
这台打字机由
小车可以执行以下五种操作:
- 如果当前单元不为空,就将其中的整数放入小车的缓冲区(缓冲区最多只能容纳一个整数)。
- 如果小车的缓冲区不为空,就将其中的整数放入当前单元(如果当前单元为空)。
- 如果小车的缓冲区和当前单元都含有整数,就交换缓冲区中的整数和当前单元中的整数。
- 将小车从当前单元
移动到下一个单元 (如果 ),缓冲区中的整数保持不变。 - 将小车重置,即将其移动到第一个单元,缓冲区中的整数保持不变。
Polycarp对这个打字机非常感兴趣,所以他请你帮助他理解它,并回答他
- 执行一个循环左移
的操作。 - 执行一个循环右移
的操作。 - 反转序列。
在每个查询之前和之后,Polycarp想要知道为了将数字分配到它们的单元(使数字
注意,Polycarp只想知道需要多少次小车重置来整理数字的位置,但实际上并不进行分配。(也即询问间互相影响)
帮助Polycarp找到每个查询的答案!
题解:
有意思的题目。和
而,考虑构造一个足够优秀的方案,使得答案足够逼近下界。
我们考虑按 实在不行直接观察样例嘛。。。
那么我们的问题就变成了:维护在操作互相关联,支持区间循环和区间翻转操作的
我们直接将区间翻转单开一个翻转后的序列,一起维护两个序列,答案则根据当前属于哪个序列就输出哪个。
首先注意到,左移
那么,问题就变成了超简化版:求出
当然,这不能暴力,但化整为零,对于每个数而言,它的贡献具有两段性。
以当前
而对于
发现答案的变化量是一个区间赋值问题,可以使用差分解决。
对于翻转后序列,直接开另一个差分即可。
int a[N],opt,n,t,k,ans,c1[N],cnt,c3[N];
signed main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=n;i++)ans+=(a[i]<i),cnt+=(a[n-i+1]<i);
print(ans);puts("");
for(int i=1;i<=n;i++){
if(a[i]<i)c1[n-i+1]--,c1[n-i+1+a[i]]++;
else if(a[i]-i+1<=n-i)c1[a[i]-i+1]++,c1[n-i+1]--;
}
c1[0]=ans;reverse(a+1,a+n+1);
for(int i=1;i<=n;i++){
if(a[i]<i)c3[n-i+1]--,c3[n-i+1+a[i]]++;
else if(a[i]-i+1<=n-i)c3[a[i]-i+1]++,c3[n-i+1]--;
}
c3[0]=cnt;
for(int i=1;i<=n;i++)c1[i]+=c1[i-1],c3[i]+=c3[i-1];
read(t);int d1=0,d2=0,tag=0;
while(t--){
read(opt);
if(opt==3)tag^=1;
else if(opt+tag==2){read(k);d2=(d2-k+n)%n;d1=(d1+k)%n;}
else {read(k);d2=(d2+k)%n;d1=(d1-k+n)%n;}
if(tag)print(c3[d2]);
else print(c1[d1]);
puts("");
}
}
唉,这个F比较水,只有*2500
- 在一定限制下排除一定不优的决策——D
- 估计答案上下界,并考虑构造尽可能接近上/下界的答案——E
- 化整为零,考虑每一个元素对答案的影响——F
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!