codeforces round #805 ABCDEFG

文章目录

A

直接模拟即可

#include<bits/stdc++.h>
// #pragma GCC optimize(2)
using namespace std;
typedef long long ll;
ll solve(){
	string s;
	cin>>s;
	s[0]-=1;
	while(s[0]=='0'&&s.size()!=1) s.erase(s.begin());
	cout<<s<<endl;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	system("pause");
}

B

使用set存储出现过的字符,当set的大小等于3时,答案加一

在这里插入代码片`#include<bits/stdc++.h>
// #pragma GCC optimize(2)
using namespace std;
typedef long long ll;
ll solve(){
	string s;
	cin>>s;
	int res=1;
	set<int>se;
	for( int i=0;i<s.size();i++){
		if(se.count(s[i])) continue;
		else{
			if(se.size()==3){
				res++;
				se.clear();
			}
			se.insert(s[i]);
		}
	}
	return res;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		cout<<solve()<<endl;
	}
	system("pause");
}`

C

找到每个数值最早的出现位置最晚的出现位置,对于询问(x,y),如果x的最早出现位置大于y的最晚出现位置则输出yes,反之输出no

#include<bits/stdc++.h>
// #pragma GCC optimize(2)
using namespace std;
typedef long long ll;
//check whether x is behind y
ll solve(){
	int n,k;
	cin>>n>>k;
	map<int,int>minn,maxx;
	vector<int>v(n);
	for( int i=0;i<n;i++){
		cin>>v[i];
	}
	for( int i=0;i<n;i++){
		maxx[v[i]]=max(maxx[v[i]],i);
		if(minn.count(v[i])==0) minn[v[i]]=i;
		else minn[v[i]]=min(minn[v[i]],i);		
	}
	for( int i=0;i<k;i++){
		int x,y;
		cin>>x>>y;
		if(minn.count(x)==0||minn.count(y)==0) cout<<"NO"<<"\n";
		else{
			if(minn[x]<maxx[y]) cout<<"YES"<<"\n";
			else cout<<"NO"<<"\n";			
		}

	}
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	system("pause");
}

D

直接模拟即可,既从价值最高的字符开始,看是否可以删除。

#include<bits/stdc++.h>
// #pragma GCC optimize(2)
using namespace std;
typedef long long ll;
ll solve(){
	string s;
	int p;
	cin>>s>>p;
	int val=0;
	vector<int>cnt(26);
	for( int i=0;i<s.size();i++){
		val+=s[i]-'a'+1;
		cnt[s[i]-'a']+=1;
	}
	for( int i=25;i>=0;i--){
		if(val-cnt[i]*(i+1)>p){
			val-=cnt[i]*(i+1);
			cnt[i]=0;
		}
		else {
			int sum=cnt[i];
			while(val-(sum-cnt[i])*(i+1)>p) cnt[i]--;
			break;
		}
	}
	string res;
	for( int i=0;i<s.size();i++){
		if(cnt[s[i]-'a'])res+=s[i],--cnt[s[i]-'a'];
	}
	cout<<res<<endl;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	system("pause");
}

E

这种集合划分的问题,一般可以转化成图来求解,类似的有二分图匹配,网络流问题。
对于此问题,可以将所给的数据抽象成一张图,对于点对(x,y),在图中对应一条边。
若使问题成立,需要满足以下条件:

  1. 首先,每个数字出现且仅出现两次,既转换后的图是一个所有点度均为2的图。
  2. 若1成立,则易证,整张图由若干个环组成,即每个点在且仅在一个环中。
  3. 在2的基础上,只需要检查每个环是否由偶数个点构成即可。(不理解可以举例或作图试一下)
#include<bits/stdc++.h>
// #pragma GCC optimize(2)
using namespace std;
typedef long long ll;
ll solve(){
	int n;
	cin>>n;
	vector<pair<int,int>>v(n);
	vector<vector<int>>g(n);
	for( int i=0;i<n;i++){
		cin>>v[i].first>>v[i].second;
		v[i].first--,v[i].second--;
		int x=v[i].first,y=v[i].second;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	vector<int>deg(n);
	for( auto it:v){
		if(it.first>=n||it.second>=n) return false;
		else{
			deg[it.first]++;
			deg[it.second]++;
		}
	}
	for( auto it:deg){
		if(it!=2) return false;
	}
	vector<int>vis(n);
	auto dfs=[&](auto self, int rt )->int{
		vis[rt]=1;
		int res=1;
		for( auto it:g[rt]){
			if(vis[it]) continue;
			res+=self(self,it);
		}
		return res;
	};
	for( int i=0;i<n;i++){
		if(!vis[i]){
			int x=dfs(dfs,i);
			if(x%2) return false;
		}
	}
	return true;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		if(solve()) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	system("pause");
}

F

首先,不论是a或者是b,若其中的某个数x是偶数,则将x替换为x/2不会对答案造成影响,因此,将ab数组进行预处理,将偶数变为奇数。
之后使用贪心算法,从大到小遍历b中的每个数(假设当前遍历到的数为x),看是否在a中出现,若出现,则将其从a中删除。反之,则令x=x/2,并再次查看。

#include<bits/stdc++.h>
// #pragma GCC optimize(2)
using namespace std;
typedef long long ll;
ll solve(){
	int n;
	cin>>n;
	vector<int>a(n),b(n);
	for( int i=0;i<n;i++){
		cin>>a[i];
		while(a[i]%2==0) a[i]/=2;
	}
	for( int i=0;i<n;i++){
		cin>>b[i];
		while(b[i]%2==0) b[i]/=2;
	}
	sort(a.begin(),a.begin()+n);
	sort(b.begin(),b.begin()+n);
	multiset<int>se;
	for( auto it:a){
		se.insert(it);
	}
	for( int i=b.size()-1;i>=0;i--){
		int x=b[i];
		while(x){
			auto it=se.find(x);
			if(it==se.end()){
				x/=2;
				continue;
			}
			else {
				se.erase(it);
				break;
			}
		}
	}
	if(se.empty()) return true;
	else return false;
	
}
int main(){
	int t;
	cin>>t;
	while(t--){
		if(solve()) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	system("pause");
}

G

本题我是用的是倍增法LCA求解,也有另一种做法,cf上有大佬使用dfs序查看一个点是否为另一个点的祖先。
这里只讲倍增LCA的做法。

对于一个集合,如果其满足题目中的条件(是一个passable集合),那么在树上来看,这个集合的所有数一定分布在一条链上(有些抽象,可以画图看一下)。

由于集合是无序的,为了让问题更好解决,可以按照每个点的高度(到根的距离)排序。之后找到距离跟最近的结点,用这个结点为根部,还原整条链。

还原的方法是:将整条链看成两条链的拼接,从高度较小的点(离根近)向高度较大的点遍历,然后将每个点加入到两条链中的一条。

注意,本题有一种情况需要特殊判断,即集合呈现出“人”字形,这种情况在题目的样例中也有出现,需要特殊处理,对应代码中的check函数的倒数第二句。

#include<bits/stdc++.h>
// #pragma GCC optimize(2)
using namespace std;
typedef long long ll;
int main(){
	int n;
	cin>>n;
	vector<vector<int>>tr(n+1);
	for( int i=0;i<n-1;i++){
		int x,y;
		cin>>x>>y;
		tr[x].push_back(y);
		tr[y].push_back(x);
	}
	vector<int>h(n+1);
	auto get_h=[&](auto self,int rt,int fa)->void{
		h[rt]=h[fa]+1;
		for( auto it:tr[rt]){
			if(it==fa) continue;
			self(self,it,rt);
		}
	};
	get_h(get_h,1,0);
	vector<vector<int>>lca(n+1,vector<int>(18));
	auto get_lca=[&](auto self,int rt,int fa)->void{
		lca[rt][0]=fa;
		for( int i=1;i<18;i++){
			lca[rt][i]=lca[lca[rt][i-1]][i-1];
		}		
		for( auto it:tr[rt]){
			if(it==fa) continue;
			self(self,it,rt);
		}	
	};
	get_lca(get_lca,1,0);
	auto LCA=[&](int x,int y)->int{
		if(h[x]>h[y]) swap(x,y);
		for( int i=17;i>=0;i--){
			if(h[lca[y][i]]>=h[x]) y=lca[y][i]; 
		}
		if(x==y) return x;
		for( int i=17;i>=0;i--){
			if(lca[x][i]!=lca[y][i]){
				x=lca[x][i];y=lca[y][i];
			}
		}
		return lca[x][0];
	};
	auto check=[&]()->int{
		int n;
		cin>>n;
		vector<pair<int,int>>v(n);
		for( int i=0;i<n;i++){
			cin>>v[i].second;
			v[i].first=h[v[i].second];
		}
		sort(v.begin(),v.begin()+n);
		int h1=v[0].second,t1=v[0].second;
		int h2=0,t2=0;
		for( int i=1;i<n;i++){
			int b=v[i].second;
			if(LCA(b,t1)==t1){
				t1=b;
			}
			else if(h2==0){
				h2=t2=b;
			}
			else if(LCA(b,t2)==t2){
				t2=b;
			}
			else return false;
		}
		if(h2!=0&&LCA(t1,t2)!=h1&&LCA(LCA(t1,t2),h1)==h1) return false;
		return true;
	};
	int t;
	cin>>t;
	while(t--){
		if(check()) {
			cout<<"YES"<<"\n";
		}
		else {
			cout<<"NO"<<"\n";
		}
	}
	
	system("pause");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值