CF1927 A~G

庆祝一下全写的正解。

赛时 1h A~E,罚坐 1h,以为 dfs 找环复杂度巨大导致没有 F。

A

link

找最左边和最右边的'B'即可,注意找不到时的处理。

#include<bits/stdc++.h>
#define int long long

template<typename T>
void read(T &x){
	 int f=1;
	 char c=getchar();
	 x=0;
	 while(c<'0'||c>'9'){
		 if(c=='-') f=-1;
		 c=getchar();
	 }
	 while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
	 x*=f;
}

template<typename T,typename I>
void chkmin(T &a,I b){
	 a=std::min(a,b);
}

template<typename T,typename I>
void chkmax(T &a,T b){
	a=std::max(a,b);
}

const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;

signed main(){
	int t;
	read(t);
	while(t--){
		int n;
		read(n);
		int l,r;
		l=-1,r=-1;
		for(int i=1;i<=n;i++){
			char c=getchar();
			if(c=='B'){
				if(l==-1) l=i,r=i;
				else r=i;
			}
		}
		printf("%lld\n",std::max(r-l+1,0ll));
	}
	return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/

B

link

\(26\) 个桶记录一下每一个字母出现的次数,不断找合法字母即可,时间复杂度 \(O(26n)\)

#include<bits/stdc++.h>
#define int long long

template<typename T>
void read(T &x){
	 int f=1;
	 char c=getchar();
	 x=0;
	 while(c<'0'||c>'9'){
		 if(c=='-') f=-1;
		 c=getchar();
	 }
	 while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
	 x*=f;
}

template<typename T,typename I>
void chkmin(T &a,I b){
	 a=std::min(a,b);
}

template<typename T,typename I>
void chkmax(T &a,T b){
	a=std::max(a,b);
}

const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;

signed main(){
	int t;
	read(t);
	while(t--){
		int n;
		read(n);
		int t[30]={0};
		for(int i=1;i<=n;i++){
			int g;
			read(g);
			int flag=0;
			for(int j=1;j<=26;j++){
				if(g==t[j]) {
					t[j]++;
					flag=j;
					break;
				}
			}
			printf("%c",(char)('a'+flag-1));
		}
		printf("\n");
	}
	return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/

C

link

明显的贪心,记录一下每个数字在哪个数组中出现过,统计一下每个数组有多少只在自己数组出现的数,如果这个数超过 \(k\) 或有数不在两个数组中出现过,则代表无解,否则一定有解。

读者自证不难。

#include<bits/stdc++.h>
#define int long long

template<typename T>
void read(T &x){
	 int f=1;
	 char c=getchar();
	 x=0;
	 while(c<'0'||c>'9'){
		 if(c=='-') f=-1;
		 c=getchar();
	 }
	 while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
	 x*=f;
}

template<typename T,typename I>
void chkmin(T &a,I b){
	 a=std::min(a,b);
}

template<typename T,typename I>
void chkmax(T &a,T b){
	a=std::max(a,b);
}

const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;

const int maxn=4e5+10;

int a[maxn],b[maxn],t1[maxn],t2[maxn];

struct node{
	int fi,se,th;
};

signed main(){
	int t;
	read(t);
	while(t--){
		int n,m,k;
		read(n),read(m),read(k);
		for(int i=1;i<=k;i++) t1[i]=t2[i]=0;
		for(int i=1;i<=n;i++) {
			read(a[i]);
			t1[a[i]]++;
		}
		for(int i=1;i<=m;i++) {
			read(b[i]);
			t2[b[i]]++;
		}
		int s1,s2;
		s1=s2=0;
		bool flag=1;
		for(int i=1;i<=k;i++){
//			printf("&&%lld %lld %lld\n",i,t1[i],t2[i]);
			if(t1[i]&&t2[i]) continue;
			if(t1[i]) s1++;
			else if(t2[i]) s2++;
			else {
				flag=0;
				break;
			}
			if(s1>k/2||s2>k/2){
				flag=0;
				break;
			}
		}
		if(flag) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/

D

link

经典套路,维护一下每个数后面第一个和它不一样的数即可,这可以从后向前递推得到。

#include<bits/stdc++.h>
#define int long long

template<typename T>
void read(T &x){
	 int f=1;
	 char c=getchar();
	 x=0;
	 while(c<'0'||c>'9'){
		 if(c=='-') f=-1;
		 c=getchar();
	 }
	 while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
	 x*=f;
}

template<typename T,typename I>
void chkmin(T &a,I b){
	 a=std::min(a,b);
}

template<typename T,typename I>
void chkmax(T &a,T b){
	a=std::max(a,b);
}

const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;

const int maxn=2e5+10;

int a[maxn],to[maxn];

namespace bcj{
	void init(int n){
		for(int i=1;i<=n+1;i++) to[i]=i;
	}
	int fa(int p){
		if(to[p]==p) return p;
		return to[p]=fa(to[p]);
	}
}

signed main(){
	int t;
	read(t);
	while(t--){
		int n;
		read(n);
		for(int i=1;i<=n;i++) read(a[i]);
		to[n]=n+1;
		for(int i=n-1;i>=1;i--){
			if(a[i]==a[i+1]) to[i]=to[i+1];
			else to[i]=i+1;
		}
		int q;
		read(q);
		while(q--){
			int l,r;
			read(l),read(r);
			if(to[l]>r) printf("-1 -1\n");
			else printf("%lld %lld\n",l,to[l]);
		}
		printf("\n");
	}
	return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/

E

link

考虑一个长为 \(k\) 个窗口,如果将其往后滑动一格,可以发现 \(a_1+a_2+\ldots+a_k\rightarrow a_2+a_3+a\ldots+a_{k+1}\),将两者作差发现 \(\operatorname{delta}=a_{k+1}-a_1\),根据题意,\(|a_{k+1}-a_1|\le 1\)。同理,继续往后移,发现 \(|a_{k+2}-a_2|\le 1\)。继续综合题目的限制,有 \(|a_{k+1}+a_{k+2}-a_1-a_2|/le 1\),因为不存在 \(a_{k+1}=a_1\),所以 \(a_{k+1}-a_1=1/-1\),综合这三个式子:

  • \(a_{k+1}-a_1=1/-1\)

  • \(a_{k+2}-a_2=1/-1\)

  • \(|(a_{k+1}-a_1)+(a_{k+2}-a_2)|/le 1\)

发现 \(a_{k+1}-a_1+a_{k+2}-a_2=0\),因此两者一定一个为 \(1\),一个为 \(-1\)

根据数学归纳法,发现若 \(a_{k+x}-a_x=1\),则 \(a_{k+x+1}-a_{x+1}=-1\),于是我们可以按照如下方法构造:

\(a_1=n,a_2=1\),则 \(a_{k+1}=n-1,a),a_{k+2}=2\),依次类推,每次往后跳 \(k\) 步,直到跳出去。

具体可以看代码实现。

#include<bits/stdc++.h>
#define int long long

template<typename T>
void read(T &x){
	 int f=1;
	 char c=getchar();
	 x=0;
	 while(c<'0'||c>'9'){
		 if(c=='-') f=-1;
		 c=getchar();
	 }
	 while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
	 x*=f;
}

template<typename T,typename I>
void chkmin(T &a,I b){
	 a=std::min(a,b);
}

template<typename T,typename I>
void chkmax(T &a,T b){
	a=std::max(a,b);
}

const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;

const int maxn=2e5+10;

int a[maxn];

signed main(){
	int t;
	read(t);
	while(t--){
		int n,k;
		read(n),read(k);
		int r=n+1,l=0,k_=k;
		if(k&1) k_=k+1;
		for(int i=1;i<=k;i++){
			if(i&1) a[i]=--r;
			else a[i]=++l;
			int z=i+k_;
			while(z<=n){
				if(i&1)a[z]=--r;
				else a[z]=++l; 
				z+=k_;
			}
		}
		for(int i=1;i<=n;i++) printf("%lld ",a[i]);
		printf("\n");
	}
	return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/

F

link

利用生成树的思想,从大到小往里面加边,如果发现成环,则更新最小答案(加边顺序使得新答案一定小于原来),即为答案。

至于寻找简单环,可以利用 dfs 来找环,注意实现的细节,一定要加上记忆化的技巧。

#include<bits/stdc++.h>
#define int long long
#define ok printf("ORz\n")

template<typename T>
void read(T &x){
	 int f=1;
	 char c=getchar();
	 x=0;
	 while(c<'0'||c>'9'){
		 if(c=='-') f=-1;
		 c=getchar();
	 }
	 while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
	 x*=f;
}

template<typename T,typename I>
void chkmin(T &a,I b){
	 a=std::min(a,b);
}

template<typename T,typename I>
void chkmax(T &a,T b){
	a=std::max(a,b);
}

const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;

const int maxm=4e5+10;

struct node{
	int nxt,to,v;
}s[maxm];

int fi[maxm];

struct node2{
	int u,v,w;
}p[maxm];

int be=0,tot=0,t;

bool cmp(node2 s1,node2 s2){
	return s1.w>s2.w;
}

int to[maxm],minans;

int go(int p){
	if(to[p]==p) return p;
	return to[p]=go(to[p]);
}

struct node3{
	int to,nxt,v;
}p2[maxm];

int F[maxm];

int tot2;

std::vector<int>ans[(int)1e4+10],mans;

bool vis[maxm],mflag=1;

void dfs(int now,int mb,bool flag){
	if(!mflag) return ;
//	printf("@@@@@%lld\n",now);
//	if(now==mb&&flag){
//		mans=ans;
//		mans.pop_back();
//		return ;
//	}
	int zz=F[now];
	while(zz){
		if(!mflag) return ;
		int to=p2[zz].to,nxt=p2[zz].nxt;
		if(vis[to]) {
			if(to==mb&&ans[t].size()>2) {
				mflag=0;
//				ans[t].pop_back();
//				mans=ans[t];
//				printf("##%lld %lld\n",mans.size(),ans[t].size());
				return ;
			}
			else {
				zz=nxt;
				continue;	
			}
		}
		vis[to]=1,ans[t].push_back(to);
		dfs(to,mb,1);
		if(mflag) ans[t].pop_back();
		zz=nxt;
	}
	return ;
}

signed main(){
//	int t;
	read(t);
	while(t--){
		int n,m;
		read(n),read(m);
		int tot=0;
		for(int i=1;i<=m;i++){
			int u,v,w;
			read(u),read(v),read(w);
			p[i].u=u,p[i].v=v,p[i].w=w;
		}
		std::sort(p+1,p+m+1,cmp);
		int fi=-1,se,vv;
		int tot3=0;
		for(int i=1;i<=n;i++) F[i]=0,vis[i]=0,to[i]=i;
		for(int i=1;i<=m;i++){
			int u=p[i].u,v=p[i].v,w=p[i].w;
			if(go(u)==go(v)) {
				fi=u,se=v,vv=w;
			}
			else to[go(u)]=go(v);
		} 
		for(int i=1;i<=m;i++){
			int u=p[i].u,v=p[i].v,w=p[i].w;
			tot3++;
//			F[u]=tot3;
			p2[tot3].to=v,p2[tot3].nxt=F[u],F[u]=tot3;
			tot3++;
			p2[tot3].to=u,p2[tot3].nxt=F[v],F[v]=tot3;
		}
//		for(int i=1;i<=n;i++) vis[i]=0;
		printf("%lld ",vv);
//		vis[fi]=1;
		minans=inf;
//		ans.shirnk_to_fit();
//		std::vector<int>ans;
		ans[t].push_back(fi);
		vis[fi]=2;
		mflag=1;
		dfs(fi,fi,0);
		printf("%lld\n",ans[t].size());
		for(int i:ans[t]) printf("%lld ",i);
		printf("\n");
	}
	return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/

G

link

非常巧妙的一道区间 dp。

\(dp_{i,j,k}\) 表示考虑完前 \(i\) 个点,未被染色的点中最左边的一个\(j\)被染色的点中最右边的一个\(k\)

先说转移。这里尽量采用递推法。分别讨论第 \(i+1\) 个点不染/向左染/向右染。明显,不染的情况可以得到 \(dp_{i,j,k}\rightarrow dp_{i,j,k}\);对于向左/右染的情况,我们更新 \(j,k\),具体实现看代码。

可以证明,这样转移会得到正确答案,读者自证不难。

现在我们考虑,为什么不能设置状态为被染色的点中最左边和最右边的分别为 \(j,k\)。如果这么设置,会出现 \(j\sim k\) 并不是一段连续的染色段,或许会出现零星的未染色段;而对于正确的状态设置方法,则会避免在这种情况下出现错误。

关于如何在考场上想到这个做法,或许要凭 OI 上的直觉了。

#include<bits/stdc++.h>
#define int long long

template<typename T>
void read(T &x){
	 int f=1;
	 char c=getchar();
	 x=0;
	 while(c<'0'||c>'9'){
		 if(c=='-') f=-1;
		 c=getchar();
	 }
	 while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
	 x*=f;
}

template<typename T,typename I>
void chkmin(T &a,I b){
	 a=std::min(a,b);
}

template<typename T,typename I>
void chkmax(T &a,T b){
	a=std::max(a,b);
}

const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;

const int maxn=110;

int a[maxn],dp[maxn][maxn][maxn],q[maxn],h[maxn];

signed main(){
	int t;
	read(t);
	while(t--){
		int n;
		read(n);
		for(int i=1;i<=n;i++) read(a[i]),q[i]=std::max(i-a[i]+1,1ll),h[i]=std::min(i+a[i]-1,n);
		memset(dp,0x3f,sizeof(dp));
		dp[0][1][0]=0;
		for(int i=0;i<n;i++){
			for(int j=1;j<=n+1;j++)
				for(int k=0;k<=n;k++) chkmin(dp[i+1][j][k],dp[i][j][k]);
			for(int j=1;j<=n+1;j++)
				for(int k=0;k<=n;k++){
					if(q[i+1]<=j) chkmin(dp[i+1][std::max(k+1,i+2)][std::max(k,i+1)],dp[i][j][k]+1);
					else chkmin(dp[i+1][j][std::max(k,i+1)],dp[i][j][k]+1);	
				}	
			for(int j=1;j<=n+1;j++)
				for(int k=0;k<=n;k++){
					if(j>=i+1) chkmin(dp[i+1][std::max(j,h[i+1]+1)][std::max(h[i+1],k)],dp[i][j][k]+1);
					else chkmin(dp[i+1][j][std::max(h[i+1],k)],dp[i][j][k]+1);
				}
		}
		printf("%lld\n",dp[n][n+1][n]);
	}
	return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/
posted @ 2024-02-07 17:02  BYR_KKK  阅读(12)  评论(0编辑  收藏  举报