Codeforces Round #782 (Div. 2)
Codeforces Round #782 (Div. 2)
A
有一个长度为 的字符串由 个字符
R
和 个字符B
组成,要求其中连续相同的字串长度最小。
考虑小学数学中的植树问题,将字符 B
插入 R
之间,把 R
分成 段。
每段 R
的长度为 ,剩余的间隔插入即可。
时间复杂度:
#define L(i,j,k) for(int i=(j);i<=(k);i++)
int n,x,y;
void work(){
cin>>n>>x>>y;
int t1=x/(y+1);
int t2=x%(y+1);
L(i,1,y){
L(j,1,t1) cout<<'R';
if(i<=t2) cout<<'R';
cout<<'B';
}L(i,1,t1) cout<<'R';
cout<<'\n';
}
B
给定 串,每次操作翻转整个序列除某一指定元素,求 次操作后使字典序最大的结果并输出方案。
考虑贪心,从低位向高位枚举,分配最少使当前位为 的操作次数即可。
举个例子:如果当前位初始为 1
,操作数 为奇数,就需要有一次指定当前位不翻转餐能保证最终的值为 。
最后特判一下最后一位要用完所有剩余操作次数的情况。
时间复杂度:
#define L(i,j,k) for(int i=(j);i<=(k);i++)
int n,k;
const int N=2e5+10;
char s[N],o[N];int a[N];
void work(){
cin>>n>>k>>(s+1);
int x=k;
L(i,1,n) a[i]=0;
L(i,1,n){
if(s[i]=='1'){
if((k&1)&&x) a[i]=1,x--,o[i]='1';
else if(!(k&1)) o[i]='1';else o[i]='0';
}else{
if(!(k&1)&&x) a[i]=1,x--,o[i]='1';
else if((k&1)) o[i]='1';else o[i]='0';
}
}if(x) a[n]+=x;
if(a[n]%2==k%2) o[n]=s[n];
else o[n]=(s[n]=='0'?'1':'0');
L(i,1,n) cout<<o[i];cout<<'\n';
L(i,1,n) cout<<a[i]<<" \n"[i==n];
}
朴素思想引出正解
C
个城市,位置分别为 ,可以把任何占领的城市作为首都,初始首都位置为 。
每次可以选择花费 将首都移动到城市 。( 为当前首都位置)
每次可以选择花费 攻占城市 。求攻占所有城市的最小花费。
还是贪心思路。
如果转移首都可以使答案更优就转移。
假设当前首都在编号为 的城市。
如果将首都转移到 ,消耗
如果不转移首都,则之后相当于转移要多消耗
比较两者的值确定决策即可。
时间复杂度:
#define L(i,j,k) for(int i=(j);i<=(k);i++)
int n,a,b;
const int N=2e5+10;
int x[N],s[N];
int dis(int a,int b){return x[b]-x[a];}
void work(){
cin>>n>>b>>a;int ans=0,c=0;
L(i,1,n) cin>>x[i];
L(i,1,n){
ans+=dis(c,i)*a;
if((n-i)*a*dis(c,i)>dis(c,i)*b)
ans+=dis(c,i)*b,c=i;
}cout<<ans<<'\n';
}
D
有 01 序列 ,进行 次操作,第 次将前 位排序,每次操作得到的序列相加得到序列 。
已知序列 ,求序列 。
首先很容易想到, 就是序列 中 的个数。
从后向前考虑, 的值只能是 或 (原因不难想到)
由此很容易知道 的值。
将其拓展到当前第 位的情况:如果 ,则 ,否则为 。
考虑如何向前递推:
因为已知第 为以及之后的情况,同时已知 的值,我们可以推算出之前区间 的个数。
考虑消除第 次操作产生的影响,假设有 个 ,回退操作相当于对 区间内的 减去 。
这个可以用树状数组维护实现,每次单点修改 位置上的值,将其减一,之后对于当前的 ,就能通过原先的 加上树状数组 的前缀和实现。即
时间复杂度:
#define L(i,j,k) for(int i=(j);i<=(k);i++)
int n,s;
const int N=2e5+10;
int a[N],c[N];
void change(int x,int k){for(;x<=n;x+=lb(x))a[x]+=k;}
int ask(int x){int s=0;for(;x;x-=lb(x))s+=a[x];return s;}
void work(){
cin>>n;L(i,1,n) cin>>c[i];
L(i,1,n) s+=c[i];s/=n;
me(a,0);R(i,n,1){
b[i]=(c[i]+ask(i)==i);
change(i-s+1,-1);s-=b[i];
}L(i,1,n) cout<<b[i]<<" \n"[i==n];
}
其实好像还有一种从前往后考虑的做法,在 CF 讨论区。
E
给定 个点, 条边的带权无向联通图。
次询问,每次询问给定两个点 ,确定某条路径,使 最小,求最小值。
首先观察样例,可以猜想是否答案只能是 中某数,即答案不大于 。
证明如下:
设某条路径上的权值组成数组 。
运用反证法,假设答案大于 ,则在 中同时出现 。
由于与运算具有不升性,如果当前假设成立,则存在 ,与基本事实不符。
Q.E.D
因为答案只有 种,只要排除了某两种可能,就可以确定答案。
- 答案为 的情况:
判断从 到 的路径中,是否存在二进制下某一位使得 中所有数在这一位上都是 。
考虑对于二进制下每一位开一个并查集维护,如果 某一位上处于同一个连通块,则答案为 。
- 答案为 的情况:
可以想到答案为 必须满足一个性质:路径上存在一个点 ,使得 。
即只要到达那个地方保证在到终点前经过一个使得与之和等于 的点,接下来就与路径无关了。
所以,我们枚举不是最低位的每一位,找到有可能存在边 使得 均为 。
这就要用到之前的并查集,同时对于每个点,记录经过这个点的所有边的边权与和 。
将同一个连通块内的所有点的 值取与和。如果与和为 则说明这个连通块内的点能使得答案为 。
时间复杂度:
#define L(i,j,k) for(int i=(j);i<=(k);i++)
int n,m,q;
const int N=1e5+100,lim=(1<<30)-1;
int z[N],x[N];bool t[N];
struct DSU{
int f[N];void cl(){L(i,1,n) f[i]=i;}
int cz(int x){return x==f[x]?x:f[x]=cz(f[x]);}
void hb(int x,int y){x=cz(x),y=cz(y);f[x]=y;}
bool ch(int x,int y){return cz(x)==cz(y);}
}s[35];
void solve(){
int l,r;cin>>l>>r;
L(i,0,29) if(s[i].ch(l,r))
return cout<<0<<'\n',void();
cout<<(t[l]?1:2)<<'\n';
}void work(){
cin>>n>>m;L(i,1,n) z[i]=lim;
L(i,0,30) s[i].cl();
L(i,1,m){
int u,v,w;cin>>u>>v>>w;
z[u]&=w,z[v]&=w;
L(j,0,29) if(w&(1<<j))
s[j].hb(u,v);
}L(i,1,29){
L(j,1,n) x[j]=lim;
L(j,1,n) x[s[i].cz(j)]&=z[j];
L(j,1,n) t[j]|=(!(x[s[i].cz(j)]));
}cin>>q;L(i,1,q) solve();
}
大胆猜想,小心证明
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)