CF Edu Round 174 题解合集
C
考虑最终合法的字符串,形如:
然后考虑对于每对 对答案的贡献是,它们中间 的个数,记为 ,。
然后考虑从左往右扫,每次遇到 往 ds 里加入一个 ,遇到 全局乘 ,遇到 计算答案。
可以用线段树维护,复杂度 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int t,n,a[200005],cnt,ans;
int val[400005],tag[400005],ls[400005],rs[400005],dcnt,rt;
void pushup(int x){
val[x]=(val[ls[x]]+val[rs[x]])%mod;
}
void pushdown(int x){
if(tag[x]==1) return;
val[ls[x]]=val[ls[x]]*tag[x]%mod;
val[rs[x]]=val[rs[x]]*tag[x]%mod;
tag[ls[x]]=tag[ls[x]]*tag[x]%mod;
tag[rs[x]]=tag[rs[x]]*tag[x]%mod;
tag[x]=1;
}
void build(int l,int r,int &x){
x=++dcnt;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,ls[x]);
build(mid+1,r,rs[x]);
pushup(x);
}
void modify1(int l,int r,int pos,int k,int x){
if(l==r){
val[x]=k;
return;
}
int mid=(l+r)>>1;
pushdown(x);
if(pos<=mid) modify1(l,mid,pos,k,ls[x]);
else modify1(mid+1,r,pos,k,rs[x]);
pushup(x);
}
void modify2(int l,int r,int ql,int qr,int k,int x){
if(ql<=l && r<=qr){
val[x]=val[x]*k%mod;
tag[x]=tag[x]*k%mod;
return;
}
pushdown(x);
int mid=(l+r)>>1;
if(ql<=mid) modify2(l,mid,ql,qr,k,ls[x]);
if(qr>=mid+1) modify2(mid+1,r,ql,qr,k,rs[x]);
pushup(x);
}
int query(int l,int r,int ql,int qr,int x){
if(ql<=l && r<=qr) return val[x];
pushdown(x);
int mid=(l+r)>>1,ans=0;
if(ql<=mid) ans=(ans+query(l,mid,ql,qr,ls[x]))%mod;
if(qr>=mid+1) ans=(ans+query(mid+1,r,ql,qr,rs[x]))%mod;
return ans;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
build(1,n,rt);
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]==1){
modify1(1,n,i,1,rt);
cnt++;
}else if(a[i]==2){
modify2(1,n,1,i,2,rt);
}else{
ans=(ans+query(1,n,1,i,rt)-cnt+mod)%mod;
}
}
cout<<ans<<'\n';
ans=0;
for(int i=1;i<=dcnt;i++){
val[i]=tag[i]=ls[i]=rs[i]=0;
}
dcnt=cnt=0;
}
}
D
考虑将两边相等的字符去掉之后,需要翻转的子串是一个前缀或者后缀。
然后就可以二分检查合法性了。
复杂度 。
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
string s;
cin >> s;
int n = s.size();
int i = 0;
while (i < n / 2 && s[i] == s[n - i - 1]) ++i;
n -= 2 * i;
s = s.substr(i, n);
int ans = n;
for (int z = 0; z < 2; ++z) {
int l = 0, r = n;
while (l <= r) {
int m = (l + r) / 2;
vector<int> cnt(26);
for (int i = 0; i < m; ++i)
cnt[s[i] - 'a']++;
bool ok = true;
for (int i = 0; i < min(n / 2, n - m); ++i) {
char c = s[n - i - 1];
if (i < m) {
ok &= cnt[c - 'a'] > 0;
cnt[c - 'a']--;
} else {
ok &= (c == s[i]);
}
}
for (auto x : cnt)
ok &= (x % 2 == 0);
if (ok) {
r = m - 1;
} else {
l = m + 1;
}
}
ans = min(ans, r + 1);
reverse(s.begin(), s.end());
}
cout << ans << '\n';
}
}
E
考虑初始只能选择 或者是 的串,能选就选了。
然后现在的问题是,对于 和 怎么选。
考虑到奇数串一定会剩下一个,而偶数串不会,所以奇数串的优先级没有偶数串高。
考虑到长度短的选完的可能性大于长度长的,所以优先选长度短的。
然后就做完了,复杂度 ,瓶颈在对子串排序。
#include<bits/stdc++.h>
using namespace std;
int t,n,b1,b2,b3,b4,cnta,cntb,vis[500005];
struct node{
int l,r,len;
};
bool cmp(node a,node b){
return a.len<b.len;
}
vector<node> v;
string s;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>s;
cin>>b1>>b2>>b3>>b4;
n=s.size();
s=" "+s;
for(int i=1;i<=n;i++){
if(s[i]=='A') cnta++;
else cntb++;
}
for(int i=1;i<n;i++){
if(s[i]=='A' && s[i+1]=='B') vis[i]++,vis[i+1]++;
if(s[i]=='B' && s[i+1]=='A') vis[i]++,vis[i+1]++;
}
for(int i=1;i<n;i++){
if(b3 && s[i]=='A' && s[i+1]=='B' && vis[i]==1 && vis[i+1]==1) b3--,cnta--,cntb--,vis[i]=0,vis[i+1]=0;
if(b4 && s[i]=='B' && s[i+1]=='A' && vis[i]==1 && vis[i+1]==1) b4--,cnta--,cntb--,vis[i]=0,vis[i+1]=0;
}
for(int i=1;i<n;i++){
if(vis[i]==1){
int j=i+1;
while(j<=n && vis[j]!=1) j++;
v.push_back((node){i,j,j-i+1});
i=j;
}
}
sort(v.begin(),v.end(),cmp);
for(int i=0;i<v.size();i++){
if((v[i].r-v[i].l+1)&1) continue;
for(int j=v[i].l;j<v[i].r;j++){
if(b3 && s[j]=='A' && s[j+1]=='B' && vis[j] && vis[j+1]) b3--,cnta--,cntb--,vis[j]=0,vis[j+1]=0;
if(b4 && s[j]=='B' && s[j+1]=='A' && vis[j] && vis[j+1]) b4--,cnta--,cntb--,vis[j]=0,vis[j+1]=0;
}
}
for(int i=0;i<v.size();i++){
if(!((v[i].r-v[i].l+1)&1)) continue;
for(int j=v[i].l;j<v[i].r;j++){
if(b3 && s[j]=='A' && s[j+1]=='B' && vis[j] && vis[j+1]) b3--,cnta--,cntb--,vis[j]=0,vis[j+1]=0;
if(b4 && s[j]=='B' && s[j+1]=='A' && vis[j] && vis[j+1]) b4--,cnta--,cntb--,vis[j]=0,vis[j+1]=0;
}
}
if(cnta<=b1 && cntb<=b2) cout<<"YES"<<'\n';
else cout<<"NO"<<'\n';
for(int i=1;i<=n;i++){
vis[i]=0;
}
cnta=cntb=0;
v.clear();
}
return 0;
}
F
考虑每一个时刻,答案为 的连通块数量 与 的连通块之差。
然后直接上线段树分治 + 可撤销并查集算就行了。
复杂度 。
常数巨大。
#include<bits/stdc++.h>
#define int long long
using namespace std;
char op;
int n,m,x,y,id,cnt[3],ans[3][400005];
map<int,int> mp[2];
struct node{
int x,y;
};
vector<node> v[800005][3];
struct line{
int x,y,fa,siz;
};
stack<line> s[3];
int ls[800005],rs[800005],dcnt,rt;
void build(int l,int r,int &x){
x=++dcnt;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,ls[x]);
build(mid+1,r,rs[x]);
}
void modify(int l,int r,int ql,int qr,int u,int y,int op,int x){
if(ql<=l && r<=qr){
v[x][op].push_back((node){u,y});
return;
}
int mid=(l+r)>>1;
if(ql<=mid) modify(l,mid,ql,qr,u,y,op,ls[x]);
if(qr>=mid+1) modify(mid+1,r,ql,qr,u,y,op,rs[x]);
}
int fa[3][400005],siz[3][400005];
void init(){
for(int i=1;i<=n;i++){
fa[0][i]=i;
fa[1][i]=i;
fa[2][i]=i;
siz[0][i]=1;
siz[1][i]=1;
siz[2][i]=1;
}
}
int find(int op,int x){
if(fa[op][x]==x) return x;
else return find(op,fa[op][x]);
}
void merge(int op,int x,int y){
x=find(op,x);
y=find(op,y);
if(x==y) return;
if(siz[op][x]>siz[op][y]) swap(x,y);
s[op].push((line){x,y,fa[op][x],siz[op][y]});
fa[op][x]=y;
siz[op][y]+=siz[op][x];
cnt[op]--;
}
void dfs(int l,int r,int op,int x){
int now=s[op].size();
for(int i=0;i<v[x][op].size();i++){
merge(op,v[x][op][i].x,v[x][op][i].y);
}
if(l==r){
ans[op][l]=cnt[op];
}else{
int mid=(l+r)>>1;
dfs(l,mid,op,ls[x]);
dfs(mid+1,r,op,rs[x]);
}
while(s[op].size()!=now){
fa[op][s[op].top().x]=s[op].top().fa;
siz[op][s[op].top().y]=s[op].top().siz;
s[op].pop();
cnt[op]++;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
cnt[0]=cnt[1]=cnt[2]=n;
build(1,m,rt);
init();
for(int i=1;i<=m;i++){
cin>>op>>x>>y;
if(x>y) swap(x,y);
int id=x*(n+1)+y;
if(op=='A'){
if(mp[0][id]){
modify(1,m,mp[0][id],i-1,x,y,0,rt);
modify(1,m,mp[0][id],i-1,x,y,2,rt);
mp[0].erase(id);
}else{
mp[0][id]=i;
}
}else{
if(mp[1][id]){
modify(1,m,mp[1][id],i-1,x,y,1,rt);
modify(1,m,mp[1][id],i-1,x,y,2,rt);
mp[1].erase(id);
}else{
mp[1][id]=i;
}
}
}
for(map<int,int>::iterator it=mp[0].begin();it!=mp[0].end();it++){
x=it->first/(n+1);
y=it->first%(n+1);
modify(1,m,it->second,m,x,y,0,rt);
modify(1,m,it->second,m,x,y,2,rt);
}
for(map<int,int>::iterator it=mp[1].begin();it!=mp[1].end();it++){
x=it->first/(n+1);
y=it->first%(n+1);
modify(1,m,it->second,m,x,y,1,rt);
modify(1,m,it->second,m,x,y,2,rt);
}
dfs(1,m,0,rt);
dfs(1,m,1,rt);
dfs(1,m,2,rt);
for(int i=1;i<=m;i++){
cout<<ans[0][i]-ans[2][i]<<'\n';
}
return 0;
}
本文作者:Kenma
本文链接:https://www.cnblogs.com/Kenma/p/18725765
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步