Codeforces Round #717 (Div. 2)
A Tit for Tat
题意很简单,我们每次操作两个数,使得这几个数拼凑出来的数的字典序最小
其实很明了,让字典序最小,我们肯定优先最前面的最小,但是我们还要增加一个数,那么我们自然就增加最后面那个使得最后面那个增大后对字典序的增加是最小的
注意不能变为负数,即使有操作数也不能操作(我就是因为这个细节才没看出来我wa哪里了wuwuwu
// Problem: A. Tit for Tat
// Contest: Codeforces - Codeforces Round #717 (Div. 2)
// URL: https://codeforces.com/contest/1516/problem/0
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// Code by: ING__
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
using namespace std;
typedef pair<int, int> PII;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int T;
int n;
int a[200];
int k;
int main(){
cin >> T;
while(T--){
cin >> n >> k;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
int cnt = 1;
for(int i = 1; i <= k; i++){
if(a[cnt]){
a[cnt]--;
a[n]++;
}
else{
// while(a[cnt] == 0 && cnt < n) // 0就别操作了,这行与下面的else效果等价
++cnt;
if(cnt >= n) break;
if(a[cnt]){
a[cnt]--;
a[n]++;
}
else{
i--;
}
}
}
for(int i = 1; i <= n; i ++){
cout << a[i] << " ";
}
cout << endl;
}
re 0;
}
B AGAGA XOOORRR
然后根据性质一想,这满足交换律还满足结合律,然后相等的几个数^
起来还是0,那么我不就直接^
就完事了吗?
// Problem: B. AGAGA XOOORRR
// Contest: Codeforces - Codeforces Round #717 (Div. 2)
// URL: https://codeforces.com/contest/1516/problem/B
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// Code by: ING__
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
using namespace std;
typedef pair<int, int> PII;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int T;
int n;
int a[2000 + 10];
int main(){
cin >> T;
while(T--){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
int x = a[1];
for(int i = 2; i <= n; i++){
x ^= a[i];
}
if(x){
puts("NO");
}
else{
puts("YES");
}
}
re 0;
}
结果是错的,例如这个样例:2 2 4 6
我们把最后两个异或起来,就变成了2 2 2这三个数异或起来就是2啊,不是0啊
原谅我太菜了到最后都没想出来
然后开了几个大佬的赛后解释
首先我这里总结一下:在满足交换律和结合律的相邻操作问题,顺序是无关的
最后再操作的时候我们最后只剩下两种情况:剩两个数相同和剩三个数相同
如果剩四个数相同的话,我们把他们两对做运算,一定也是相等的;如果剩五个相同的,那么其中三个与起来,根据上面图片的第4个性质,我们可以知道这三个数异或起来还是这个数,自然就成了三个数
因为顺序是无关的,并且只是相邻操作,那么我们只需要看看这一组数里能不能分成两组或三组满足这个性质了
那么我们可以相当于在里面插入分隔线,来形成分组
只需要判断每组间的异或和是否相等即可
判断每组我们就可以先对他们造一个前后缀异或就好了
// Problem: B. AGAGA XOOORRR
// Contest: Codeforces - Codeforces Round #717 (Div. 2)
// URL: https://codeforces.com/contest/1516/problem/B
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// Code by: ING__
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
using namespace std;
typedef pair<int, int> PII;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int T;
int n;
int a[2000 + 10];
int q[2000 + 10];
int h[2000 + 10];
int main(){
cin >> T;
while(T--){
memset(q, 0, sizeof(q));
memset(h, 0, sizeof(h));
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
for(int i = 1; i <= n; i++){
q[i] = q[i - 1] ^ a[i];
}
for(int i = n; i >= 1; i--){
h[i] = h[i + 1] ^ a[i];
}
int ok = 0;
for(int i = 1; i < n; i++){ // 两个数的情况,
if(q[i] == h[i + 1]){
ok = 1;
break;
}
}
//分为1 ~ i | (i + 1) ~ j - 1 | (j) ~ n
for(int i = 1; i < n; i++){
for(int j = i + 2; j <= n; j++){
if(q[i] == h[j] && (q[i] ^ q[j - 1]) == q[i]){ // 这里要参考类似前缀和算法的性质,配合着上面异或的性质来进行的中间的选取,a[l ~ r] = a[r] ^ a[l - 1];
// 根据这个性质也可以不构造后缀
ok = 1;
}
}
}
if(ok){
puts("YES");
}
else{
puts("NO");
}
}
re 0;
}
C Baby Ehab Partitions Again
这个题是让你构造一个数列,删除一些数后,分成两个部分,使得任意划分这两个部分的总和都不会相等
在可分的情况下,这些数的总和显然必须是一个偶数,毕竟只有偶数才能整除2
如果总和是一个偶数,并且数组里面的元素也都是偶数,那么可以对每个元素都除以2,这也相当于对总和除以了2,那么如果这个现在的和不是一个偶数,那么显然这不需要剔除任何的数他都是成立的
相反,如果是一个偶数怎么办呢?
我们只需要把数组里面其中一个奇数给剔除掉了就好了,毕竟偶数减奇数就是一个奇数了
但是例如下面的样例:
1 1 1 5
这个数的总和是一个偶数,但是他任意组合是并不能够想等的
那么现在我就不需要剔除任何的数
即我现在的数组,找不到这样的一个序列,它们的和为sum/2
那么这时候我怎么判断呢?
这时候我们就要采用01背包的背包方程来判断每个数选与不选。对这组数来看看不用去除某个数的话,能不能把我们的sum/2凑出来;
可能我这里说的不是很详细,就是我们选取了一些数看看能不能凑成sum/2,如果能凑成,那么必然另一半的和也一定是sum/2,所以上就转移为每个数选与不选看看是否能凑成sum/2的容量了
// Problem: C. Baby Ehab Partitions Again
// Contest: Codeforces - Codeforces Round #717 (Div. 2)
// URL: https://codeforces.com/contest/1516/problem/C#
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
using namespace std;
typedef pair<int, int> PII;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int n;
int a[2000 + 10];
int f[210000 + 100];
int summ;
bool check(){
for(int i = 1; i <= n; i++){
for(int j = 201000; j >= a[i]; j--){
f[j] = max(f[j], f[j - a[i]] + a[i]); // 我占用a[i]的体积获得了a[i]的价值
}
}
if(f[summ / 2] == summ / 2) return 0; // 在前m个物品里选,是不是能用summ / 2 的体积来凑出来summ / 2的价值来
else return 1;
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
int g = a[1];
for(int i = 2; i <= n; i++){
g = __gcd(g, a[i]);
}
for(int i = 1; i <= n; i++){
a[i] /= g;
summ += a[i];
}
if(summ & 1){
cout << 0 << endl;
re 0;
}
int id = 0;
for(int i = 1; i <= n; i++){
if(a[i] & 1){
id = i;
break;
}
}
if(!check()){
cout << 1 << endl;
cout << id;
}
else{
cout << 0;
}
re 0;
}