Educational CF round174(div2)总结

得分总结:场切4道罚时257rank1624
主要问题:

  1. B题没读懂题吃了罚时。
  2. A,B题多测都没有清空,各吃一发罚时。
  3. D题犯唐了,翻转后没有调用,吃四发罚时简直不是人

更新:2.20 增加F题解


链接:A
我们考虑什么情况下 b 数组是不合法的,当出现 bi 使得 bi1=bi+1=1bi=0 时b是不合法的,因为 i 位与 i+1i1位明显矛盾了。
我们直接判断b的每一位,出现不合法就输出No。
AC记录
ACcode:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 110
// By flyfreemrn
inline ll read(){
	ll x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
inline void write(ll x){
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
ll b[MAXN];
ll n,T,ans;
void work(){
	n=read()-2,ans=1;
	memset(b,0,sizeof(b));
	for(int i=1;i<=n;i++){
		b[i]=read();
	}
	for(int i=1;i<=n;i++){
		if(b[i-1]==1&&b[i+1]==1&&b[i]==0){
			cout<<"NO\n";
			return;
		}
	}
	cout<<"YES\n";
	return;
}
int main(){
	T=read();
	while(T--)work();
	return 0;
}

链接:B
首先,题目所给的操作类似于黑白棋盘染色,这提示我们同一种颜色至多两次操作就能全部转化成另一种颜色,第一次选全部在白格上的,第二次选全部在黑格上的,就一定能补缺补漏的转化。
所以,对于每一种颜色,有两种情况。

  1. 没有相同颜色的格子相接,也就是所有这种颜色的格子都在黑格或白格上,对于这种颜色一次操作就能转化。
  2. 出现相同颜色的格子相接,我们需要两次操作转化。
    因为最后要转化为同一种颜色,所以我们可以去除其中一种颜色的贡献,特判一下 1 还是 2

AC记录
ACcode:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 1010
#define pir pair<ll,ll>
// By flyfreemrn
inline ll read(){
	ll x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
inline void write(ll x){
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
ll addx[4]={0,1,0,-1},addy[4]={1,0,-1,0};
ll n,m,T,sum,res;
ll col[MAXN][MAXN],vis[MAXN][MAXN];
map <ll,ll> t;
set <ll> st;
void work(){
	n=read(),m=read();
	sum=0,res=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)col[i][j]=read(),st.insert(col[i][j]),t[col[i][j]]=0;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int u=0;u<=3;u++){
				ll edx=i+addx[u],edy=j+addy[u];
				if(edx>n||edy>m||edx<1||edy<1)continue;
				if(col[i][j]==col[edx][edy])t[col[i][j]]=1;
			}
		}
	}
	for(auto it=st.begin();it!=st.end();it++){
		if(t[(*it)]){
			sum+=2,res=2;
		}else sum+=1;
	}
	cout<<sum-res<<endl;
	st.clear();
	return;
}
int main(){
	T=read();
	while(T--)work();
	return 0;
}

链接:C

先考虑暴力情况

如果有一个 1 和一个 3 中间隔了 x2 的话,他们对答案的贡献显然等于

Cx1+Cx2+...+Cxx

=2x1

每找到一个 1 后,我们向后找所有的 3 并统计路径上 2 的个数,直接计入答案。
时间复杂度 O(n2)

优化

我们可以考虑将每一个序列的贡献都记在结尾的 3 上(当然也可以拆到 1 上)。
加入一个 3 前面有 Pi 个隔了 i21
那么他对答案的贡献就是

i=0nPi(2i1)

我们发现,这个值是可以递推计算的,我们记上式值为 sum
当我们找到一个 2 时,有

P0>P1,P1>P2...Px>Px+1

那么每一个 1 对于答案的贡献都会 2+1
所以我们额外记当前位置 1 的个数为 cnt
那么 sum 就有

sum=sum2+cnt

当我们找到一个 1 时,有

P0++,cnt++

当我们找到一个 3 时,直接把 sum 累加到答案即可。
最后别忘了取模。
AC记录
ACcode:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 998244353
#define MAXN 200010
// By flyfreemrn
inline ll read(){
	ll x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
inline void write(ll x){
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
ll	cnt1,cnt2,sum;
ll n,T,ans;
void work(){
	n=read(),ans=cnt1=cnt2=sum=0;
	for(int i=1;i<=n;i++){
		ll a=read();
		if(a==1){
			cnt1++;
//			sum++;
		}else if(a==2){
			sum=(sum*2+cnt1)%mod;
		}else{
			ans=(ans+sum)%mod;
		}
	}
	cout<<ans<<endl;
	return;
}
int main(){
	T=read();
	while(T--)work();
	return 0;
}

链接:D
回文串的题,我们先记中间位置为 mid(因为字符串长度为偶数,所以 mid 这个位置是不存在的,mid+1mid1 是相邻的)。
我们可以发现,原串中一些位置与对应的位置是相同的,这些位置不需要调整,而一些位置不相同,这些位置必须被调整。
所以我们找到 l,r 分别表示 mid+l 位置第一次出现不对应的情况,mid+r 位置最后一次出现不对应的情况。
接下来,我们分类讨论

  1. 找不到不对应的位置,原串就是回文的,直接输出 0
  2. mid+lmid+r 位置与 midrmidl 位置的字母数量完全相同,换句话说 mid+lmid+rmidrmidl 的一个排序,这表示我们只需要调整 mid+lmid+r 就可以了。
  3. 除此以外,调整的区间就必须包含 midrmidlmid+lmid+r 中的一个全部和另一个的一部分
    我们先从右往左找,我们枚举区间左端点 i 让我们可以通过调整 imid+r 使原串回文,再从左往右找,这时候只需要反转一下原串然后重复一遍就可以了。

AC记录
ACcode:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 200010
// By flyfreemrn
inline ll read(){
	ll x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
inline void write(ll x){
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
char c[MAXN],s[MAXN];
ll l,r,n,t[30],T;
ll work(){
//	n=read();
	for(int i=1;i<=26;i++)t[i]=0;
//	cin>>(c+1);
//	n=strlen(c+1);
	l=n,r=0;
	for(ll i=1;i<=n/2;i++){
		ll x=(n/2)-i+1,y=(n/2)+i;
		if(c[x]==c[y])continue;
		l=min(l,i),r=max(r,i);
	}
	if(l==n&&r==0){
//		cout<<"0\n";
		return 0;
	}
	for(int i=l;i<=r;i++){
		ll x=(n/2)-i+1,y=(n/2)+i;
		ll a=c[x]-'a'+1,b=c[y]-'a'+1;
		t[a]--,t[b]++;
	}
	ll type=0;
	for(int i=1;i<=26;i++){
		if(t[i])type=1;
	}
	if(!type){
//		cout<<r-l+1<<endl;
		return r-l+1;
	}
//	for(int i=1;i<l;i++){
//		ll x=(n/2)-i+1,y=(n/2)+i;
//		ll a=c[x]-'a'+1,b=c[y]-'a'+1;
//		t[a]++,t[b]++;
//	}
	for(int i=1;i<=r;i++){
		ll x=(n/2)-i+1;
		ll a=c[x]-'a'+1;
		t[a]+=2;
		type=0;
		for(int j=1;j<=26;j++){
			if(t[j]<0||t[j]%2){
				type=1;
			}
		}
		if(!type){
//			cout<<i+r<<endl;
			return i+r;
		}
	}
//	cout<<2*r<<endl;
	return 2*r;
}
int main(){
	T=read();
	while(T--){
		cin>>(s+1);
		n=strlen(s+1);
		for(int i=1;i<=n;i++)c[i]=s[i];
		ll ans=work();
		for(int i=1;i<=n;i++){
//			swap(s[i])
			c[i]=s[n-i+1];
		}
		ans=min(ans,work());
		cout<<ans<<endl;
	}
	return 0;
}

链接:E
逆天分讨,不想写了(


链接:F
我们把题意转化一下,就是在 A 图和 B 图种加边删边,要求满足 B 图中连通的点 A 图中也要连通,问我们每次操作后至少还要加多少条边才能满足条件。

我们先考虑问题的简化版本。

假如现在只有加边操作,如何计算答案,因为我们要处理无向图的连通性问题,能想到并查集
先处理 B 图加边的操作,将 A 图存到并查集里,每次向 B 中加边时,如果两个点在 A 中不连通,就将答案++。
接下来我们处理 A 图的操作,同时为了防止以上操作重复计算答案,我们可以再开一个并查集,将我们前面加过的虚边也加进去。
这样,我们往 A 加边时先判断两点在第一个并查集中是否连通,再判断两点在第二个并查集中是否连通来统计答案。
同时,因为我们把虚边也存下来了,所以在 B 图中加边时也要考虑第二个并查集中的连通性。

正解

对于删边操作,我们可以转化一下,先对操作离线,假如我们在 i 时刻加入一条边,在 j 时刻删除这条边,就相当于这条边在 ij 存在。
这就是很显然的线段树分治了,直接套上就能过了。
AC记录
ACcode:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 400010
#define pir pair<ll,ll>
#define dpir pair<pir,ll>
#define ls first
#define rs second
// By flyfreemrn
inline ll read(){
	ll x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
inline void write(ll x){
	if(x>9)write(x/10);
	putchar(x%10+'0');
}

//回溯栈 
struct node_modify{
	ll pos,fa,siz,type;//0A图 1B图 
}t[MAXN*10]; 
ll id;

//并查集 
struct node_dsu{
	ll fa[MAXN],siz[MAXN],type;
	ll get(ll x){
		return fa[x]==x?x:get(fa[x]);
	}
	void merge(ll x,ll y){//合并 
		x=get(x),y=get(y);
		t[++id]=(node_modify){y,fa[y],siz[y],type};
		t[++id]=(node_modify){x,fa[x],siz[x],type};
		if(siz[x]>siz[y]){
			fa[y]=x;
			siz[x]+=siz[y];
		}else{
			fa[x]=y;
			siz[y]+=siz[x];
		}
	}
	void build(ll x){
		for(int i=1;i<=x;i++)fa[i]=i,siz[i]=1;
	}
}dsu[2];//0加虚边,1只存A图 

//线段树分治 
struct node_sgt{
	ll l[MAXN*4],r[MAXN*4];
	ll ans[MAXN];
	vector <dpir> vec[MAXN*4];
	void build(ll lz,ll rz,ll now){
		l[now]=lz,r[now]=rz;
		if(lz==rz)return;
		ll mid=(lz+rz)>>1;
		build(lz,mid,now<<1);
		build(mid+1,rz,(now<<1)+1);
	}
	void insert(ll lz,ll rz,ll now,dpir val){//加边 
		if(l[now]>=lz&&r[now]<=rz){
			vec[now].push_back(val);
			return;
		}
		ll mid=(l[now]+r[now])>>1;
		if(lz<=mid)insert(lz,rz,now<<1,val);
		if(rz>mid)insert(lz,rz,(now<<1)+1,val);
	}
	void clear(ll stp){//回溯 
		while(id>stp){
			dsu[t[id].type].fa[t[id].pos]=t[id].fa;
			dsu[t[id].type].siz[t[id].pos]=t[id].siz;
			id--;
		}
	}
	void work(ll now,ll p){//处理 
		ll stp=id;
		for(int i=0;i<vec[now].size();i++){
			ll x=vec[now][i].ls.ls,y=vec[now][i].ls.rs,type=vec[now][i].rs;
			if(type==1){
				x=dsu[1].get(x),y=dsu[1].get(y);
				if(x==y)continue;
				if(dsu[0].get(x)!=dsu[0].get(y))p++;
				dsu[1].merge(x,y);
			}else{
				x=dsu[0].get(x),y=dsu[0].get(y);
				if(x==y)continue;
				ll x1=dsu[1].get(x),y1=dsu[1].get(y);
				if(x1==y1)p--;
				else dsu[1].merge(x1,y1);
				dsu[0].merge(x,y);
			}
		}
		if(l[now]==r[now]){
			ans[l[now]]=p;
			clear(stp);
			return;
		}
		work(now<<1,p);
		work((now<<1)+1,p);
		clear(stp);
	}
}sgt;

//主函数
ll n,q,idx;
ll st[MAXN],ed[MAXN];
dpir line[MAXN];
map <pir,ll> mp[2];
int main(){
	scanf("%lld%lld",&n,&q);
	dsu[0].build(n),dsu[0].type=0;
	dsu[1].build(n),dsu[1].type=1;
	sgt.build(1,q,1);
	for(int i=1;i<=q;i++){
		char c;
		ll x,y;
		cin>>c>>x>>y;
		ll type=(c=='B'?1:0);
		if(mp[type][make_pair(x,y)]){
			ll now=mp[type][make_pair(x,y)];
			mp[type][make_pair(x,y)]=mp[type][make_pair(y,x)]=0;
			ed[now]=i-1;
		}else{
			mp[type][make_pair(x,y)]=mp[type][make_pair(y,x)]=++idx;
			line[idx]=make_pair(make_pair(x,y),type);
			st[idx]=i;
		}
	}
	for(int i=1;i<=idx;i++){
		if(!ed[i])ed[i]=q;
		sgt.insert(st[i],ed[i],1,line[i]);
	}
	sgt.work(1,0);
	for(int i=1;i<=q;i++)cout<<sgt.ans[i]<<endl;
	return 0;
}

posted @   flyfreemrn  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示