Codeforces 903 div3 A-F

A

题目分析

数据范围很小,暴力枚举即可,然后给字符串x的长度设置一个上限,我设了50,因为n*m<25,多一倍够用了

C++代码

#include<iostream>
using namespace std;
void solve(){
    int n,m;
    string x,s;
	cin>>n>>m>>x>>s;
	int cnt=0;
	while(x.size()<50){
		for(int i=0;i<x.size();i++)
			if(x[i]==s[0]&&x.size()-i>=m){
				if(x.substr(i,m)==s){
					cout<<cnt<<endl;
					return;
				}
			}
		cnt++;
		x+=x;
	}
	cout<<-1<<endl;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

B

题目分析

找三个数中的最小数,然后判断其他两个数是否是他的倍数,如果不是,直接输出no

否则直接判断要切多少次,次数大于三就不可以

C++代码

#include<iostream>
using namespace std;
int main(){
	int t;
	cin>>t;
	while(t--){
		int a,b,c;
		cin>>a>>b>>c;
		int minn=min(min(a,b),c);
		if(a%minn||b%minn||c%minn)puts("NO");
		else{
			int cnt=a/minn-1+b/minn-1+c/minn-1;
			if(cnt>3)puts("NO");
			else puts("YES");
		}
	}
	return 0;
}

题目分析

 旋转90度与原来相同,可以发现,旋转的过程中,每个数都与另外三个数相关联,如图(i,j)

与他相关联的三个位置也标出来了,直接把四个字母都变成其中最大的一个字母就可以了,然后操作数直接加上它们与最大的字母的差距

C++代码

#include<iostream>
using namespace std;
const int N=1010;
char g[N][N];
char rev[N][N];
int n;
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%s",g[i]+1);
	int cnt=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			char &v1=g[i][j],&v2=g[j][n-i+1],&v3=g[n-i+1][n-j+1],&v4=g[n-j+1][i];
			int maxx=max(max(v1,v2),max(v3,v4));
			cnt+=maxx-v1;
		}
	cout<<cnt<<endl;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

D

题目分析

每次找a[i]、a[j]、x,使得a[i]=a[i]/x,a[j]=a[j]*x

由此可见,相当于将a[i]的一个因数分给a[j]

每个数是由若干个质数的乘积组成,所以可以对每个a[i]分解质因数,然后判断每个质数出现的次数是否是n的倍数,如果是,则可以让所有数相等,否则不能

拿样例举例:

100=(2^2)*(5^2)

2=(2^1)

50=(2^1)*(5^2)

10=(2^1)*(5^1)

1无法分解质因数,不过不影响

可以发现,2一共有5次幂,5也一共有5次幂,都是n=5的倍数,每个数只需得到一个2一个5即可,即每个数都可以变成10

C++代码

#include<iostream>
#include<map>
using namespace std;
const int N=10010;
int a[N];
int n;
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	map<int,int> cnt;
	for(int i=1;i<=n;i++){
		int s=a[i];
		for(int j=2;j<=s/j;j++){//分解质因数
			if(s%j==0){
				while(s%j==0){
                    cnt[j]++;//j的次数+1
					s/=j;
				}
			}
		}
		if(s>1)cnt[s]++;
	}
	for(auto t:cnt){
		if(t.second%n!=0){
			puts("No");
			return;
		}
	}
	puts("Yes");
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

题目分析

dp[i]:从n到i最少需要删除几个数才可以变成优美序列

从后往前做一遍DP,每次遍历到一个数,有两种情况:

1、不选择该数开头的一串序列

dp[i]=dp[i+1]+1

2、选择该数开头的一串序列,即下标为i~i+a[i]的数都不能删

dp[i]=min(dp[i],dp[i+a[i]+1])

记得初始化

dp[n]=1;//只有一个数的时候只能删掉,因为a[i]至少为1,后面最少要跟着一个数

dp[n+1]=0;

C++代码

#include<iostream>
using namespace std;
const int N=200010;
int dp[N],a[N];
int n;
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	//dp[i]表示从n~i最少删除几个数 
	dp[n+1]=0; 
	dp[n]=1;//最后一个数不可能选,因为它后面没有数了 
	for(int i=n;i>=1;i--){
		//不选以a[i]为起点的一串数字,就要删掉a[i]
		dp[i]=dp[i+1]+1;
		//选以a[i]为起点的一串数字,前提是i+a[i]要小于等于n
		if(i+a[i]<=n)dp[i]=min(dp[i],dp[i+a[i]+1]); 
	}
	cout<<dp[1]<<endl;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

题目分析

换根DP

设置一个数组f[N][2]

f[i][0]:以i为根节点到任意一个被标记的点的最大距离

f[i][1]:以i为根节点到任意一个被标记的点的次大距离

dfs1(u,fa)函数由子节点更新父节点的信息

假设j是u的儿子节点,状态转移方程为:

1、f[j][0]+1>=f[u][0]:f[u][1]=f[u][0],f[u][0]=f[j][0]+1

2、f[j][0]+1>f[u][1]:f[u][1]=f[j][0]+1

dfs2(u,fa)函数由父节点更新子节点的信息

1、f[fa][0]不是由f[u][0]更新来的,则用f[fa][0]更新u:

      f[fa][0]+1>=f[u][0]:f[u][1]=f[u][0],f[u][0]=f[fa][0]+1

      f[fa][0]+1>f[u][1]:f[u][1]=f[fa][0]+1

2、f[fa][0]是由f[u][0]更新来的,则用f[fa][1]更新u:

      f[fa][1]+1>=f[u][0]:f[u][1]=f[u][0],f[u][0]=f[fa][1]+1

      f[fa][1]+1>f[u][1]:f[u][1]=f[fa][1]+1

两次dfs传入的第一个参数必须相同(1~n中的任何一个数),不然会打乱父子关系

C++代码

#include<iostream>
using namespace std;
const int N=200010,M=N*2,INF=0x3f3f3f3f;
int h[N],e[M],ne[M],idx;
int f[N][2];//f[u][0]存储以节点u为根节点的到其他点的最大值,f[u][1]存储次大值 
int n,k,ans;
void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs1(int u,int fa){//子节点更新父节点 
    //由于初始化f为负无穷(被标记的点除外)
    //所以此时求到的最大和次大的距离就是到每个标记的点的距离
	for(int i=h[u];i!=-1;i=ne[i]){
		int j=e[i];
		if(j==fa)continue;
		dfs1(j,u);
		if(f[j][0]+1>=f[u][0])f[u][1]=f[u][0],f[u][0]=f[j][0]+1;
		else if(f[j][0]+1>f[u][1])f[u][1]=f[j][0]+1;
	}
}
void dfs2(int u,int fa){//父节点更新子节点 
	if(fa!=-1){
		int t=f[fa][0]+1;//t为父节点的最大值+1 
		//如果父节点的最大值刚好就是由u过来的,则t更新为次大值+1 
		if(f[fa][0]==f[u][0]+1)t=f[fa][1]+1;
        
        //父节点信息更新字节点信息
		if(t>=f[u][0])f[u][1]=f[u][0],f[u][0]=t;
		else if(t>f[u][1])f[u][1]=t;
	}
    //更新u的所有儿子
	for(int i=h[u];i!=-1;i=ne[i]){
		int j=e[i];
		if(j==fa)continue;
		dfs2(j,u);
	}
	ans=min(ans,f[u][0]);
}
void solve(){
	cin>>n>>k;
	idx=0,ans=INF;
	for(int i=1;i<=n;i++)
		f[i][0]=f[i][1]=-INF,h[i]=-1;
	for(int i=1;i<=k;i++){
		int x;
		cin>>x;
		f[x][0]=f[x][1]=0;
	}
	for(int i=1;i<n;i++){
		int a,b;
		cin>>a>>b;
		add(a,b),add(b,a);
	}
    //注意:dfs1和dfs2中的参数必须相同,不能上面传1下面传2,不然两次dfs的父子关系就不一样
	dfs1(1,-1);
//	for(int i=1;i<=n;i++)
//		cout<<"i="<<i<<" "<<f[i][0]<<" "<<f[i][1]<<endl;
	dfs2(1,-1);
//	for(int i=1;i<=n;i++)
//		cout<<"i="<<i<<" "<<f[i][0]<<" "<<f[i][1]<<endl;
	cout<<ans<<endl;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
}

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Codeforces Round 894 (Div. 3) 是一个Codeforces举办的比赛,是第894轮的Div. 3级别比赛。它包含了一系列题目,其中包括题目E. Kolya and Movie Theatre。 根据题目描述,E. Kolya and Movie Theatre问题要求我们给定两个字符串,通过三种操作来让字符串a等于字符串b。这三种操作分别为:交换a中相同位置的字符、交换a中对称位置的字符、交换b中对称位置的字符。我们需要先进行一次预处理,替换a中的字符,然后进行上述三种操作,最终得到a等于b的结果。我们需要计算预处理操作的次数。 根据引用的讨论,当且仅当b[i]==b[n-i-1]时,如果a[i]!=a[n-i-1],需要进行一次操作;否则不需要操作。所以我们可以遍历字符串b的前半部分,判断对应位置的字符是否与后半部分对称,并统计需要进行操作的次数。 以上就是Codeforces Round 894 (Div. 3)的简要说明和题目E. Kolya and Movie Theatre的要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Codeforces Round #498 (Div. 3) (A+B+C+D+E+F)](https://blog.csdn.net/qq_46030630/article/details/108804114)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Codeforces Round 894 (Div. 3)A~E题解](https://blog.csdn.net/gyeolhada/article/details/132491891)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值