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