CF round1005(div2)总结

ps:本人被D题硬控1个半小时直接洪文,半夜掉大分,此博客是在极佳快死了的状态下写出来的,有一些类似人类的语言自行理解

比赛链接

更新:12.17添加E题解

链接:A
首先,我们可以将所有连续的1合并为1个,因为这些1都可以在同一次操作中去除
然后,我们考虑,对于不在末尾的1,因为我们要保证原序列只剩0,所以我们需要两次操作,先把1和后面都移过去,在把0移回来
而对于末尾的1,直接移过去就好了
所以我们直接统计1的个数*2再特判一下就好了
AC记录
ACcode:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 1010
// 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 T,n;
char c[MAXN];
void work(){
	n=read();
	cin>>(c+1);
	ll ans=0;
	for(int i=1;i<=n;i++){
		if(c[i]=='1'&&c[i+1]!='1'){
			ans+=1+(i<n);
		}
	}
	cout<<ans<<endl;
	return;
}
int main(){
	T=read();
	while(T--)work();
	return 0;
}

链接:B
把得分转化一下,一个序列的得分就是序列里每个前面已经出现过相同数的数的个数
所以,我们可以发现,删去其中一部分后,序列得分只降不增
那其实我们的问题就转化成了找一个最大的区间,这个区间中的所有数都只出现过一次,删去这些数不会对得分产生任何影响
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');
}
ll T,n;
ll a[MAXN],pre[MAXN];
map <ll,ll> mp;
void work(){
	n=read();
	ll ans=0;
	for(int i=1;i<=n;i++){
		a[i]=read();
		mp[a[i]]++;
	}
	for(int i=1;i<=n;i++){
		if(mp[a[i]]>1){
			pre[i]=0;
		}else pre[i]=pre[i-1]+1;
		if(pre[i]>pre[ans]){
			ans=i;
		}
	}
	for(int i=1;i<=n;i++)mp[a[i]]=0;
	if(ans==0)cout<<"0\n";
	else cout<<ans-pre[ans]+1<<" "<<ans<<endl;
	return;
}
int main(){
	T=read();
	while(T--)work();
	return 0;
}

链接:C
根据取正数和取负数分别删除前缀后缀的特性,我们可以构造贪心的解法,那就是从两边往中间选
具体来说,我们指定一个位置x最后选,先从1到x选正数,再从n到x选负数
这样的话我们处理一下前缀和后缀和,然后枚举位置x,取最大值即可
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');
}
ll T,n;
ll a[MAXN],pre[MAXN],suf[MAXN];
void work(){
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	pre[0]=suf[n+1]=0;
	for(int i=1;i<=n;i++){
		pre[i]=pre[i-1];
		if(a[i]>0)pre[i]+=a[i];
	}
	for(int i=n;i;i--){
		suf[i]=suf[i+1];
		if(a[i]<0)suf[i]-=a[i];
	}
	ll ans=0;
	for(int i=1;i<=n;i++)ans=max(ans,suf[i]+pre[i]);
	cout<<ans<<endl;
	return;
}
int main(){
	T=read();
	while(T--)work();
	return 0;
}

链接:D
傻逼fhx多测没清空本地找了一个小时,找到以后有因为逆天小细节吃了6发罚时,最后D题喜提674超高分
首先,看到异或运算,我们想到先拆位
然后,我们发现,这是一道比大小的题目,我们从最高位往最低位考虑
假如我们考虑完前面几位后已经吃掉了任意个粘液,那么最多再吃掉一个当前位为1的数,因为吃掉以后当前位就变成0了
所以我们从最高位考虑到最低位,记录已经吃掉了几个粘液,如果吃掉这些粘液后当前位仍然为1,那么就可以继续往前吃一个,从后往前找到原序列中第一个当前位为1的数,把他后面的都吃掉
所以我们考虑预处理一个pre数组,prei,j表示原序列第i个即以前二进制下第j位最后一个位1的位置,同时处理一个后缀和suf数组方便快速求当前位的值
但是,我们发现一个严重的问题,那就是二进制下所有位置的限制是同时生效的,也就是假如在i位吃完以后我们后面只能吃第i位为0的数了
我们可以记录一个maxz,表示前面所有位中对答案限制的最大值,我们吃到maxz就不能往下吃了
最后就是一些小细节了,关于maxz的处理和往前吃的过程可以看ACcode
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');
}
ll pre[MAXN][50],suf[MAXN][50],mp[50];
ll a[MAXN],n,q;
void work(){
	n=read(),q=read();
	for(int i=0;i<=30;i++)mp[i]=0,suf[n+1][i]=0,pre[0][i]=0;
	for(int i=1;i<=n;i++){
		a[i]=read();
		for(int j=0;j<=30;j++){
			if(a[i]&(ll)(1ll<<j))mp[j]=i;
			pre[i][j]=mp[j];
		}
	}
	for(int i=n;i;i--){
		for(int j=0;j<=30;j++){
			suf[i][j]=suf[i+1][j];
			if(a[i]&(ll)(1ll<<j))suf[i][j]^=1;
		}
	}
	for(int i=1;i<=q;i++){
		ll qs=read(),now=n,maxz=0;
		for(int j=30;j>=0;j--){
			ll p=(qs&(ll)(1ll<<j))?1:0;
			p^=suf[now+1][j];
			if(p>0){
				now=pre[now][j];
				if(now<=maxz)break;
				maxz=max(maxz,pre[now-1][j]);
			}else if(a[now]&(ll)(1ll<<j))break;
			else{
				maxz=max(maxz,pre[now][j]);
			}
			if(!j)now--;
		}
		cout<<n-max(maxz,now)<<" ";
	}
	cout<<endl;
}
int main(){
	ll T=read();
	while(T--)work();
	return 0;
}

链接:E
首先,我们可以知道,每一行的颜色是不能更换的,因为第一列不会下落,如果我们更换了每一行的颜色,那么第一列一定不符合要求
其次,什么时候两行交换位置不会对最终的形态造成影响,当我们找到颜色相同的两行i,j且i,j中间所有不同颜色行的长度都小于i,j
这个时候,i,j两行的沙块会在最终的三角形中相接,可以自己手模一边
这样的话,一行对于答案的影响是所有满足上述条件的行的个数,讲所有行的贡献相乘就是答案
我们可以从长度小的行开始,统计完长度为i的行后把这一行删去,这样,当我们遍历到一行i时,所有与i满足条件的行j都会与i联通(中间颜色都相同
我们可以用链表维护删边,然后用并查集维护连通块大小,删除一行后判断两端颜色是否相同,相同的话就将并查集合并,这一行对答案的贡献也就等于连通块大小
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');
}
struct node_dsu{
	ll fa[MAXN],siz[MAXN],col[MAXN];
	void getin(ll id){
		fa[id]=id,siz[id]=1,col[id]=read();
	}
	ll get(ll x){
		if(fa[x]==x)return x;
		else return fa[x]=get(fa[x]);
	}
	void merge(ll x,ll y){
		x=get(x),y=get(y);
		if(col[x]!=col[y]||x==y)return;
		fa[x]=y;
		siz[y]+=siz[x];
	}
}dsu;
struct node_chain{
	ll pre[MAXN],nxt[MAXN];
	void build(ll x){
		for(int i=2;i<x;i++){
			pre[i]=i-1,nxt[i]=i+1;
		}
		pre[1]=-1,nxt[1]=2;
		pre[x]=x-1,nxt[x]=-1;
	}
	void erase(ll pos){
		nxt[pre[pos]]=nxt[pos];
		pre[nxt[pos]]=pre[pos];
		dsu.siz[dsu.get(pos)]--;
		if(nxt[pos]!=-1&&pre[pos]!=-1)dsu.merge(pre[pos],nxt[pos]);
	}
}chain;
ll n,T,ans;
ll pos[MAXN];
void work(){
	ans=1;
	n=read();
	for(int i=1;i<=n;i++){
		ll p=read();
		pos[p]=i;
	}
	for(int i=1;i<=n;i++){
		dsu.getin(i);
		dsu.merge(i,i-1);
	}
	chain.build(n);
	for(int i=1;i<=n;i++){
//		cout<<pos[i]<<" "<<dsu.get(pos[i])<<" "<<dsu.siz[dsu.get(pos[i])]<<endl;
		ans=ans*dsu.siz[dsu.get(pos[i])]%mod;
		chain.erase(pos[i]);
	}
	cout<<ans<<endl;
	return;
}
int main(){
	T=read();
	while(T--)work();
	return 0;
}


posted @   flyfreemrn  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示