CF Edu Round 174 题解合集

here.

C

考虑最终合法的字符串,形如:

1,2,2,2,,2,2,2,3

然后考虑对于每对 1,3 对答案的贡献是,它们中间 2 的个数,记为 cnt2cnt1

然后考虑从左往右扫,每次遇到 1 往 ds 里加入一个 1,遇到 2 全局乘 2,遇到 3 计算答案。

可以用线段树维护,复杂度 O(nlogn)

#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

考虑将两边相等的字符去掉之后,需要翻转的子串是一个前缀或者后缀。

然后就可以二分检查合法性了。

复杂度 O(nlogn)

#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

考虑初始只能选择 AB 或者是 BA 的串,能选就选了。

然后现在的问题是,对于 ABABBABA 怎么选。

考虑到奇数串一定会剩下一个,而偶数串不会,所以奇数串的优先级没有偶数串高。

考虑到长度短的选完的可能性大于长度长的,所以优先选长度短的。

然后就做完了,复杂度 O(nlogn),瓶颈在对子串排序。

#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

考虑每一个时刻,答案为 A 的连通块数量 与 AB 的连通块之差。

然后直接上线段树分治 + 可撤销并查集算就行了。

复杂度 O(nlognα(n))

常数巨大。

#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 中国大陆许可协议进行许可。

posted @   _Kenma  阅读(13)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起