Codeforces Round #696 (Div. 2)

A. Puzzle From the Future (构造+贪心)

 
题目链接:传送门
 

&nsbp;
题意:给你一个二进制的数b,问你怎么凑出一个a使得c=a+b的二进制数最大,注意此处的加和并不会产生进位,换句话说, 每一位上加和的结果可能是0,1,2三种情况,并且这里连续相同的数字会缩减成一位eg:122221 => 121
 
解题思路:很明显不管b是什么,a的第一位肯定是1,这样才能凑出最大的c,又因为连续相同的数字会缩减成一位,所以我们可以根据前面一位的数字得出当前数字的选取,比如第一位是2,当前b的这一位是0,那我们可以选1,否则选0,其他情况亦是如此,更具前一位推后一位
Code:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t,n;
	string ch;
	scanf("%d",&t);
	while(t--) {
		scanf("%d",&n);
		cin>>ch;
		putchar('1');
		int last = 1 + (ch[0] -'0');//表示的是前一种状态的值
		for(int i = 1;i < n; ++i) {
			if(last == 2) {//分为三种情况讨论
				if(ch[i] == '1')
					putchar('0');
				else
					putchar('1');
				ch[i] = '0';
				last = 1;
			}
			else if(last == 1) {
				if(ch[i] == '1') {
					putchar('1');
					ch[i] = '2';
					last = 2;
				}
				else {
					putchar('0');
					last = 0;
				}
				
			}
			else if(last == 0) {
				putchar('1');
				if(ch[i] == '0')
					last = 1;
				else if(ch[i] == '1')
					last = 2;
				ch[i] = '1';
			}
		}
		putchar('\n');
	}
	
	return 0;
}

 

B. Different Divisors (欧拉筛 + 思维)

 
题目链接:传送门
 

 
解题思路:题目比较简短,我就不描述了,我们要找到满足这样的数a,很明显我们发现其实就是找到满足这两条规定的素数,因为他这里把1个本身也算进去了,所以我们只需要找到满足条件的两个素数即可,我们通过欧拉筛,筛出一定范围内的素数,然后找到满足素数x,y,使得 \(x-1>=d且y-x>=d且x*y-y>=d\)
Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long

queue<int> que;

const int N = 1000005;
int prime[N];
bool vis[N];

void get_prime() {
    memset(vis,true,sizeof vis);
    memset(prime,0,sizeof prime);
    vis[0] = vis[1] = false;
    for(int i = 2; i <= N; ++i) {
        if (vis[i]) {
            prime[++prime[0]] = i;
        }
        for(int j = 1;j <= prime[0] && i*prime[j] <= N; ++j) {
            vis[i * prime[j]] = false;
            if (i % prime[j] == 0)
                break;
        }
    }
}
ll slove(ll d) {
	ll ans = 1;
	ll last = 1;//表示前一位的因子,第一个因子肯定是1
	int cnt = 0;
	for(int i = 1;i < prime[0]; ++i) {
		if(prime[i] - last >= d) {
			last = prime[i];
			ans *= last;
			cnt++;
		}
		if(cnt == 2)//找到两个即可
			break;
	}
	return ans;
}

int main()
{
	int t;
	get_prime();
	scanf("%d",&t);
	ll n;
	while(t--) {
		scanf("%lld",&n);
		printf("%lld\n",slove(n));
	}
	
	return 0;
}

 

C. Array Destruction(思维+枚举)

 
题目链接:传送门
 

 
题意:给你一个2n长的数组,每次可以抛出两个价值和为x的数,并且使得x的值刷新为max(k1,k2),问你是否能将数组的数全部抛空
解题思路:我们可以通过观察发现,数组中要是有超过两个元素不能被数组中其他元素枚举出来,就肯定不能全部抛空,更进一步我们会发现,数组每次抛出元素都是先抛出最大的元素,不然先抛了小的元素,大的元素就抛不了了,此时最大的问题便是第一个x怎么确定,其实第一个x肯定是由数组中最大的元素和一个其他的元素构成的,这个其他的元素我们不能直接得出,只能通过枚举从第一个元素枚举到第2n-1个元素,对这些x进行操作,找到了满足条件的x就跳出循环,当我们确定了第一个x,那么后续的x也就一一确定了,第二个x一定是a[2n-1],然后后续的x就是max(x,x-y),具体操作请看代码
Code:

#include<bits/stdc++.h>
using namespace std;

struct Node {
	int x,y;
};

int n,t,k;
vector<int> V;

bool fg(vector<int> a, int x) {
	multiset<int> S;
	int key = x;
	for(auto it : a) {
		S.insert(it);
	}
	vector<Node> ans;
	for(int i = 0;i < n; ++i) {//判断当前传进来的x是否满足凑出n对
		auto it1 = S.end();//找到集合最大的元素
		it1--;
		int y = x - *it1;//x对应的另一半
		S.erase(it1);
		auto it2 = S.find(y);
		if(it2 == S.end()) {//如果在凑的中途有一个x没有凑出,那么就是当前的传进俩的x不能全部抛出
			return true;
		}
		S.erase(it2);
		ans.push_back({x-y,y});
		x = max(x-y,y);
	}
	puts("YES");
	printf("%d\n",key);
	for(int i = 0,len = ans.size();i < len; ++i) {
		printf("%d %d\n",ans[i].x,ans[i].y);
	}
	return false;
}


int main()
{
	scanf("%d",&t);
	while(t--) {
		V.clear();
		scanf("%d",&n);
		for(int i = 0;i < 2 * n; ++i) {
			scanf("%d",&k);
			V.push_back(k);
		}
		sort(V.begin(),V.end());
		bool is = true;
		for(int i = 0;i < 2 * n - 1; ++i) {
			int x = V[i] + V[2 * n - 1];
			is = fg(V,x);
			if(!is)
				break;
		}
		if(is) {
			puts("NO");
		}
	}
	return 0;
}

 
OVER,上了117分,开心ing

posted @ 2021-01-20 11:57  MangataTS  阅读(73)  评论(0编辑  收藏  举报