Codeforces Round #764 (Div. 3)

A. Plus One on the Subset

分析:
由于一次可以选中任意多个元素加一;所以本题其实要求的就是序列中最大值和最小值的差

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x7fffffff
int main(){
	int t;cin>>t;
	while(t--){
		int n,minn=INF,maxx=-INF;
		cin>>n;
		for(int i=1;i<=n;i++){
			int x;cin>>x;
			minn=min(minn,x);
			maxx=max(maxx,x);
		}
		cout<<maxx-minn<<endl;
	}
	return 0;
} 

B. Make AP

分析:
假设我们开始输入的是a,b,c;最后选中三个数中任意一个数乘于正整数m(注意m只能是正整数),使最后的a’,b’,c’满足等差序列的性质即a’+c’==2*b’。由于a、b或c都有可能需要乘于m,所以显然有三种情况:
1、a * m +c ==2* b
2、a + c == 2* b * m
3、a +c * m == 2* b

分别对三种结果做处理,则会出现以下三种判别方式(除法结果大于0是为了保证m是正整数):
1.(2 * b-c)%a ==0&&(2b-c)/a>0
2.
(a+c)% (2b) ==0&&(a+c)/(2b)>0
3.
(2 * b-a)%c ==0&&(2b-a)/c>0

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x7fffffff
int main(){
	int t;cin>>t;
	while(t--){
	    int a,b,c; cin>>a>>b>>c;
	    int m=2*b-c,p=2*b-a,flag=0;
	    if(m/a>0&&m%a==0||p/c>0&&p%c==0||(a+c)/(2*b)>0&&(a+c)%(2*b)==0) cout<<"YES"<<endl;
	    else cout<<"NO"<<endl;
	}
	return 0;
} 

C. Division by Two and Permutation

分析:
由于n<=50,我们可以考虑一下怎么样才能暴力遍历1…n就能得到结果呢? 我们可以先预处理1…n之间的数据i,找到每一个i可能得到的值x(1<=x<=n),并将记录x的值加一,即cnt[x]++预处理结束后,我们便得到了长度为n的序列要想在一系列处理之后变成序列1…n,那么其中每一个数i的最小出现次数为cnt[i]。然后对我们输入的序列进行处理,同样记录他通过处理可能得到的1…n之间的数的个数,将其结果记录在vector容器g[1…n]中;最后遍历一遍1~n,如果至少出现过一次g[i].size()<cnt[i],那么跳出循环,输出“NO”,否则输出“YES”。
时间复杂度为:O(nlog2n)

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x7fffffff
int main(){
	int t;cin>>t;
	while(t--){
		vector<int>g[100];
	    int cnt[100],n;
		cin>>n; memset(cnt,0,sizeof(cnt));
		for(int i=1;i<=n;i++){
			int x=i;
			while(x>=1){
				cnt[x]++;
				x/=2;
			}
		}
	    for(int i=1;i<=n;i++){
	    	int x;cin>>x;
	    	while(x>=1){
	    		if(x<=n) g[x].push_back(i);
	    		x/=2;
			}
		}
		bool flag=true;
		for(int i=1;i<=n;i++){
			if(g[i].size()<cnt[i]){
				flag=false;
				break;
			}
		}
		if(flag) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
} 

D. Palindromes Coloring

分析:
刚开始写蒻蒻写这道题的时候毫无思路,并且还有点烦躁,但是后面一看提交,发现有三千多个提交了,我就稳定心来决定要做出这道题。当然,我也很快就AC掉了。那我们一起来分析一下吧!!!
本题题意是将通过k种颜色将长度为n的字符串分成k堆,每一堆之间的字符可以随意调换位置任意多次,并且不一定每一个字符都需要被涂上颜色(这是一个很好的条件,将题目简化了许多),让我们求出这几堆中最短的回文字符串的最大长度。看到这,估计很多小伙伴跟我开始写之前一样,还是挺懵的。别着急,我们慢慢来看。
首先我们可以明确一个要求,要让最短的回文字符串的长度最大,那么我们需要做的就是将回文字符串尽量平均分给每一个堆。我们可以很容易求出n/k是平均分成k堆以后最短的字符串的最大长度;因为被染颜色的字符个数是<=n的。接下来就是将回文串进行均分了。我们知道,两个相同的字符一定是回文串,我们记它为一对;所以我们可以遍历一遍字符串找到字符串中所有的对数ou,(注意:aaaa相当于是两对),最后k堆中的每一堆均分到的对数为ou/k,这些字符长度为ou / k* 2 ; 最后一个细节就是,只有一个字符或者有(x对和一个字符)都是回文串;所以最后的结果就是min(n/k,ou /k * 2+1)

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x7fffffff
int main(){
	int cnt[30],t;cin>>t;
	while(t--){
		int n,k; cin>>n>>k;
		int ans=n/k;
		int ou=0,res;
		memset(cnt,0,sizeof(cnt)); 
		string str; cin>>str;
		sort(str.begin(),str.end());
		for(int i=0;i<str.length();i++) cnt[str[i]-'a']++;
		for(int i=0;i<26;i++) ou+=cnt[i]/2;
	    ans=min(ans,ou/k*2+1);
		cout<<ans<<endl;
	}
	return 0;
} 

接下来几道就是蒻蒻看完大佬思路以后的补题了,蒻蒻还是太菜了,以后还需要更加努力!!!


G. MinOr Tree

分析: 一道思维+暴力的题目。
总结规律吧:我发现只要是位运算的题目求最值,解法绝大部分都是从高位到低位暴力处理每一位
拿本题为例,本题需要求出一颗边权值做位或以后值最小的生成树。也就是位运算的最值问题。我们可以从高位到低位开始遍历每一位如果边权值没有该位的所有边能构成一棵生成树,那么该位无效,可以标记一下所有边权值带该位的边为已访问,这样接下来就不用考虑这些边了;否则如果边权值没有该位的所有边不能构成一棵生成树,说明最后的答案里一定包含该位,所以可以在最后结果里直接加上该位。最后一个问题了,那么如何判断是否能构成生成树呢?我们可以用并查集的方法,把所有未标记并且边权值不包括该位的边合并在一起,最后看看所有的结点1~n是否在同一个连通图里,如果在,说明可以构成生成树,否则不可以
时间复杂度:O(30*(2*m+n)),也就是O(m+n)。

#include<bits/stdc++.h>
using namespace std;
#define INF 0x7fffffff
const int maxn=2e5+100;
struct Edge{
	int from,to,w;
}g[maxn];
int n,m,fa[maxn];
bool vis[maxn];
inline int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline void Union(int u,int v){
	if(find(u)==find(v)) return ;
	fa[find(u)]=find(v);
}
void Init(){
	for(int i=1;i<=n;i++) fa[i]=i;
}
bool check(int x){
	Init();
	for(int i=1;i<=m;i++){
		if(vis[i]||g[i].w&(1<<x)) continue;
		int u=g[i].from,v=g[i].to;
		Union(u,v); Union(v,u);
	}
	for(int i=1;i<=n;i++){
		if(find(1)!=find(i)) return false;
	}
	return true; //没有该边可以构成生成树 
}
void solve(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		vis[i]=false;
		cin>>g[i].from>>g[i].to>>g[i].w;
	}
	int res=0;
	for(int i=30;i>=0;i--){
		//没有这条边可以构成生成树
		if(check(i)){
			for(int j=1;j<=m;j++) if(g[j].w&(1<<i)) vis[j]=true;
		}
		else res+=(1<<i); 
	}
	cout<<res<<endl;
}
int main(){
	int t; cin>>t;
	while(t--){
		cout<<endl;
		solve();
	} 
	return 0;
} 
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&が&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值