Codeforces805(A-G题解)

目录

A题        Round Down the Price

B题        Polycarp Writes a String from Memory

C题        Train and Queries

D题        Not a Cheap String

E题        Split Into Two Sets

F题        Equate Multisets

G题(G1,G2)        Passable Paths


A题

题意:给定你一个数字让你减去一个10^k次后(注意k最大化),这个数最后会变成多少。

代码实现:

void solve()
{
	string s;
	cin >> s;
	s[0]--;
	cout << atol(s.c_str()) << endl;
}

B题

 题意:给定你一个字符串,并且你每次最多只能删除3种不同类型的字符,问你最后把这个字符串变成空串需要多少次。

代码实现:

void solve()
{
	int ans = 0;
	set<char>st;
	string s;
	cin >> s;
	for(auto x:s){
		if(st.count(x)) continue;
		if(st.size() >= 3){
			ans++;
			st.clear();
		}
		st.insert(x);
	}
	if(st.size()) ans++;
	cout << ans << endl;
}

C题

题意:给定你一个序列,k次询问,每次询问都会给出l,r,表示是否能从l到r,(l能到r当且仅当l在序列中的编号小于r的编号)

代码实现:

void solve()
{
	int n,m;
	cin >> n >> m;
	map<int,int> mp1,mp2;
    //mp1维护序列里面的某个数字的最小值
    //mp2维护序列里面的某个数字的最大值
	for(int i = 1;i <= n;i++){
		int x;
		cin >> x;
		if(!mp1[x]) mp1[x] = i;
		mp2[x] = i;
	}
	while(m --){
		int a,b;
		cin >> a >> b;
		if(!mp1[a]){
			puts("No");
			continue;
		}
		int c,d;
		c = mp1[a];
		if(!mp2[b]) {
			puts("No");
			continue;
		}
		d = mp2[b];
		if(c > d) puts("No");
		else puts("Yes");
	}
}

D题

题意: 给定你一个字符串,从a-z的价值分别为1-26,现在问你最少删除多少次能把这个字符串的价值变到少于或等于p,如果能,则输出删除后的字符串。

题解:这题我的思路是贪心的从e往前找,如果能删除,则记录,最后循环跑一遍记录答案。

void solve()
{
    //ch记录原字符串中包含i+'a'这个字符的个数
    //del记录要删除原字符串中要删除的字符的个数
	memset(ch,0,sizeof(ch));
	memset(del,0,sizeof(del));
	ll k,tot = 0;
	string s;
	cin >> s;
	cin >> k;
	for(auto x:s) {
		ch[x-'a'+1]++;
		tot += (x-'a'+1);
	}
	for(int i = 26;i >= 1;i--){
		while(tot > k && ch[i]){
			tot -= i;
			ch[i]--;
			del[i]++;
		}
	}
	string ans = "";
	for(auto x:s){
		if(del[x-'a'+1]) {
			del[x-'a'+1]--;
			continue;
		}
		ans += x;
	}
	cout << ans << endl;
}

E题

 题意:给定你一些牌,让你把这些牌分为两组,且这两组里面每一组的数字不能有重复的,如果不能分成两组请输出NO,否则输出YES

 题解:仔细分析,分为两组这个条件,就是在告诉我们给定我们这么多条边,问我们能不能构造成一个二分图。了解二分图的人应该都知道二分图的一个充要条件是二分图中不能含有奇数环。、那奇数环是个什么意思呢?----(就是有奇数个点的环)

证明:我们用染色法画图,用1表示左边,2表示右边,最后会发现图会成这样(1和1出现在了同一边上,说明左边的点和左边的点有连边,因此不能构成二分图)

                      

代码实现:

//我采用的是并查集找环,维护环中点的数量。
int f[200010],sz[200010],d[200010];
int find(int x)
{
	return x == f[x] ? x : f[x] = find(f[x]);
}
void merge(int x1,int x2)
{
	int y1 = find(x1),y2 = find(x2);
	if(y1 != y2) {
		sz[y2] += sz[y1];
		f[y1] = y2;	
	} 
}
void solve()
{
	int n,u,v;
	cin >> n;
	for(int i = 1;i <= n;i++) {
		f[i] = i;
		sz[i] = 1;
		d[i] = 0;
	}
	for(int i = 1;i <= n;i++) {
		cin >> u >> v;
		d[u]++,d[v]++;
		merge(u,v);
	}
	for(int i = 1;i <= n;i++){
        //当奇数环的时候,或者某个点已经出现三次,则不能构成题目所需
		if(sz[find(i)] % 2 || d[i] > 2) {
			puts("NO");
			return ;
		}
	}
	puts("YES");
}

F题

题意:给定我们两个序列,问在a不动的情况下,能否用b数组里面的值进行乘2或者除2的操作让b等于a

题解:我们分析什么情况下b不能变成a,首先对于一个数x,如果他能变成一个比他大的数y,那么y必然是一个偶数,因为x比y小,需要不断乘2,乘2就会变成偶数,因此y > x 且 y 为奇数,那么不可能存在把b变成a的方案。因此这个题的做法就是维护一个a的大根堆,一个b的大根堆,如果发生了刚刚a数组里面的数大于b数组里的数,并且a是奇数则输出NO,否则把最大的那个点拿出来除2,然后push进去继续比较即可!!! 

代码实现:

void solve()
{
	int n;
	ll x,t;
	priority_queue<ll> que1,que2;
	cin >> n;
	for(int i = 1;i <= n;i++) {
		cin >> x;
		que1.push(x);
	}
	for(int i = 1;i <= n;i++) {
		cin >> x;
		que2.push(x);
	}
	while(que1.size() && que2.size()){
		if(que1.top() == que2.top()){
			que1.pop();que2.pop();
			continue;
		}
		if(que1.top() > que2.top()){
			if(que1.top() & 1){
				puts("NO");
				return ;
			}
			t = que1.top();
			t /= 2;
			que1.pop();
			que1.push(t);
			continue;
		}
		if(que1.top() < que2.top()){
			t = que2.top();
			que2.pop();
			que2.push(t / 2);
		}
	}
	if(que1.size() || que2.size()) puts("NO");
	else puts("YES");
}	

G题(G1,G2)

题意:给定你一颗树,有q次询问,问你每一次询问是否能不重复经过一条边,把所有序列中的点都遍历到。 

题解:对于这个题目,我们知道如果我们能找到这个序列的最深的两个点,并且走一遍,我们就能知道能不能把所有序列中的点都遍历到。

考虑两种情况

情况一(所有点处于一条链上)

       

对于上面这张图,我们要找的序列是1,2,5,那么我们直接找到1,2,5这个序列最大的深度的点,一直往上面走,既可以走到我们的序列。

第二种情况(有两条链)

     

我们要查找的序列为3,6,5,我们还是找到深度最深的点5和6依次向上跑即可找到所需序列。

我们分析上图可发现,如果一个序列都处于一条链上,那么这里面的点都会有一个公共的祖先节点;如果一个序列处于两条链上,那么这个序列上的点要么处于第一条链,要么处于第二条链,否则就是不能完成不经过重复边跑这条路径。

反例:比如说这个图,他没有在两条链上但是他们的最近公共祖先一样,我们要访问蓝色的点,但是中间那个点却不在左右两边任意一条链上,因此也是不行的。

代码实现:

#include "bits/stdc++.h"
using namespace std;
const int N = 200010;
struct node{
	int nxt;
	int to;
}e[N << 1];
int log2n = (int)(log(N) / log(2) + 0.5);
int deep[N],fa[N][30],he[N],cnt,p[N];
void add(int u,int v)
{
	e[++cnt].to = v;
	e[cnt].nxt = he[u];
	he[u] = cnt;
}
void get(int now,int father)
{
	deep[now] = deep[father] + 1;
	fa[now][0] = father;
	for(int i = 1;(1 << i) <= deep[now];i++) {
		fa[now][i] = fa[fa[now][i - 1]][i - 1];
	}
	for(int i = he[now];~i;i = e[i].nxt){
		if(e[i].to == father) continue;
		get(e[i].to,now);
	}
}
int lca(int u,int v)
{
	if(deep[u] < deep[v]) swap(u,v);
	int d = deep[u] - deep[v];
	for(int i = 0;(1 << i) <= d;i++) {
		if((1 << i) & d) u = fa[u][i];
	}

	if(u == v) return u;
	for(int i = log2n;i >= 0;i--) {
		if(fa[u][i] != fa[v][i]) {
			u = fa[u][i];
			v = fa[v][i];
		}
	}
	return fa[u][0];
}
void solve()
{
	int n,u,v,q,v1,v2,t;
	memset(he,-1,sizeof(he));
	cnt = 0;
	cin >> n;
	for(int i = 1;i <= n - 1;i++) {
		cin >> u >> v;
		add(u,v);add(v,u);
	}
	get(1,0);
	cin >> q;
	while(q--){
		cin >> n;
		v1 = v2 = -1;
		for(int i = 1;i <= n;i++) {
			cin >> p[i];
			if(i == 1) v1 = p[i];
			else if(deep[p[i]] > deep[v1]) v1 = p[i];
		}
		for(int i = 1;i <= n;i++) {
			if(p[i] != v1) {
				t = lca(p[i],v1);
				//看是否会有两条链
				if(t != p[i] && (v2 == -1 || deep[p[i]] > deep[v2])) v2 = p[i];
			}
		}
		//在一条链上
		if(v2 == -1){
			puts("Yes");
			continue;
		}

		bool ok = true;
		int r = lca(v1,v2);
		for(int i = 1;i <= n;i++) {
			int r1 = lca(p[i],v1);
			int r2 = lca(p[i],v2);
			//如果p[i]既不在r1这条链,也不在r2这条链,说明p[i]不可以一次性通过
			if(!((r1 == p[i] && r2 == r) || (r1 == r && r2 == p[i]))) ok = false;
		}
		if(ok) puts("Yes");
		else puts("No");
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int t = 1;
	while(t--){
		solve();
	}
	return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值