Codeforces Round #647 (Div. 2) A~D题解

本文详细解析了Codeforces Round #647 (Div. 2)的A到D四道题目,包括Johnny与古董计算机、爱好、评级下降和贡献的问题。涉及的算法包括数论、位操作和图论,通过示例和AC代码阐述了解题思路。
摘要由CSDN通过智能技术生成

目录

A. Johnny and Ancient Computer

B. Johnny and His Hobbies

C. Johnny and Another Rating Drop

D. Johnny and Contribution


A. Johnny and Ancient Computer

题意:给定两个数A,B,问A能不能不断通过乘或除2、4、8(能整除才能除),得到B

思路:如果A能得到B,B也能得到A,所以AB的大小关系假定为A大B小。如果A不能整除B,则不可能得到B,否则计算出AB倍数,为了让步数最小先除8再除4再除2,如果最后剩的数非1,则也不能得到B。

AC代码:

/*---------------------------------
 *File name: A.cpp
 *Creation date: 2020-06-05 09:18
 *Link:https://codeforces.com/contest/1362/problem/A 
 *-------------------------------*/
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair<int, int>
#define Pque priority_queue 
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const double EPS = 1e-8;

int main(){
	int t;
	scanf("%d", &t);
	while(t--){
		LL a , b;
		scanf("%lld %lld", &a, &b);
		LL ans = 0;
		if(a < b) swap(a, b);
		if(a % b != 0) printf("-1\n");
		else {
			LL times = a / b;
			while(times % 8 == 0) times /= 8, ans++;
			while(times % 4 == 0) times /= 4, ans++;
			while(times % 2 == 0) times /= 2, ans++;
			if(times != 1) printf("-1\n");
			else printf("%lld\n", ans);
		}
	}
	return 0;
}

B. Johnny and His Hobbies

题意:给定n个各不相同的数集,问能不能选择一个K,使得每个数异或K之后的数集与原数集相同(顺序可以不同)。

思路:暴力直接做,从小到大枚举K,异或的结果与原数集比较。

AC代码:

/*---------------------------------
 *File name: B.cpp
 *Creation date: 2020-06-05 09:18
 *Link:https://codeforces.com/contest/1362/problem/B
 *-------------------------------*/
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair<int, int>
#define Pque priority_queue 
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const double EPS = 1e-8;

int main(){
	int t;
	scanf("%d", &t);
	while(t--){
		int n;
		scanf("%d", &n);
		set<int> s;
		map<set<int>, bool> mp;
		for(int i = 0; i < n; ++i){
			int a ;
			scanf("%d", &a);
			s.insert(a);
		}
		mp[s] = 1;
		bool flag = 0;
		for(int i = 1; i <= 2000; ++i){
			set<int> :: iterator it;
			set<int> c;
			for(it = s.begin(); it != s.end(); ++it){
				c.insert((*it) ^ i);
			}
			if(mp[c] == 1){
				printf("%d\n", i);
				flag = 1;
				break;
			}
		}
		if(!flag) printf("-1\n");
	}
	return 0;
}

C. Johnny and Another Rating Drop

题意:给一个N,问二进制下0~N的所有数中相邻的数的二进制同一位不同的个数和。如0101和1110有3个数位不同。

思路:\small 2^0这一位每一位相邻的数都不一样,对答案的贡献为N,\small 2^1这一位每两位对答案贡献位1...类推,当N有cnt位二进制位的时候,每\small 2^{cnt - 1}对答案贡献为1,所以计算出N有多少位二进制,然后算每一位对答案的贡献就行了。

AC代码:

/*---------------------------------
 *File name: C.cpp
 *Creation date: 2020-06-05 09:18
 *Link:https://codeforces.com/contest/1362/problem/C
 *-------------------------------*/
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair<int, int>
#define Pque priority_queue 
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const double EPS = 1e-8;

LL fpow(LL x, LL y){
	LL ans = 1;
	while(y){
		if(y & 1) ans = ans * x;
		x *= x;
		y >>= 1;
	}
	return ans;
}

int main(){
	int t;
	scanf("%d", &t);
	while(t--){
		LL n;
		scanf("%lld", &n);
		int cnt = 0;
		LL tmp = n;
		while(tmp) cnt++, tmp >>= 1;
		//n++;
		LL ans = 0;
		for(int i = 0; i < cnt; ++i){
			ans += n / fpow(2, i);
		}
		printf("%lld\n", ans);
	}
	return 0;
}

D. Johnny and Contribution

题意:Jony要写n篇博客,每篇博客的内容只能覆盖一个主题,但一个主题可以被多篇博客覆盖。在博客与博客之间可以相互引用,但是相互引用的博客不能涵盖相同的主题,否则博客是无效的。给出Jony要写的博客之间的相互引用关系,他在写博客的时候会依照这个引用关系来安排写博客的顺序,因此他在写博客的时候会首先查看他要引用的博客的主题,然后选出没有被覆盖的且主题编号最小的主题写入本篇博客。例如:若要的这篇博客引用的博客覆盖了主题1,3,2,5,那么这篇博客就应该要选择主题4编写。问是否存在一个写博客的顺序满足博客之间的相互引用。

思路:题意精简之后就是有N个点M条边,每个点有特定属性Ti,对于任意一点u,与u直接相连的所有点中不能有与点u属性相同的点,并且直接相连的点的属性必须存在1~\small T_u-1的所有数。那么首先比较简单的可以判断的是与u直接相连的点中是否存在属性一样的点,只要记录下所有点的属性值就可以。第二个要判断的是是否存在1~\small T_u-1在与点u相连的点中,只需要将与点u相连的点的属性放到set里,然后1~\small T_u-1去查询就可以了。如果这两个条件都满足,则就可以安排出顺序。但是由于具体的安排需要的是:对于当前要安排的位置P,要求是1~P-1中与安排在这个位置的点u相连的点中不能有重复属性且必须1~\small T_u - 1全都出现,所以可以sort升序排序属性值,根据属性值从小到大安排,这样只要满足以上两个条件,此安排就必定可行。

AC代码:

/*---------------------------------
 *File name: C.cpp
 *Creation date: 2020-06-05 09:18
 *Link:https://codeforces.com/contest/1362/problem/C
 *-------------------------------*/
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair<int, int>
#define Pque priority_queue 
using namespace std;
const int maxn = 5e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const double EPS = 1e-8;

int head[maxn], tot = -1;
struct Edge{
	int v, next;
	void Add_Edge(int u, int v){
		this -> v = v;
		this -> next = head[u];
		head[u] = tot;
	}
}edge[maxn << 1];
struct Node{
	int T;
	int num;
	friend bool operator < (Node x, Node y){
		return x.T < y.T;
	}
}t[maxn];
int pt[maxn];

int main(){
	int n, m;
	scanf("%d %d", &n, &m);
	memset(head, -1, sizeof(head));
	for(int i = 1; i <= m; ++i){
		int a, b;
		scanf("%d %d", &a, &b);	
		edge[++tot].Add_Edge(a, b);
		edge[++tot].Add_Edge(b, a);
	}
	for(int i = 1; i <= n; ++i) {
		scanf("%d", &t[i].T);
		pt[i] = t[i].T;
		t[i].num = i;
	}
	sort(t + 1, t + 1 + n);
	bool flag = 1;
	for(int i = 1; i <= n; ++i){
		set<int> s;
		int u = t[i].num;
		for(int j = head[u]; j != -1; j = edge[j].next){
			s.insert(pt[edge[j].v]);
			if(pt[u] == pt[edge[j].v]){
				flag = 0;
				break;
			}
		}
		if(!flag) break;
		int cnt = 0;
		while(++cnt < pt[u]){
			if(s.find(cnt) == s.end()){
				flag = 0;
				break;
			}
		}
		if(!flag) break;
	}
	if(!flag) printf("-1\n");
	else{
		for(int i = 1; i <= n; ++i) printf("%d%c", t[i].num, i == n ? '\n' : ' ');
	}
	return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值