凌晨睡不着 练cf算了
A. Tit for Tat
题意:一行数字,最多k个操作,每次操作可以把不同的两个数一个+1一个-1,并且操作完数字不能为负,问字典序最小的方案。
sb题,选头部数字减尾部数字加就行。
(然后sb题还因为sb码力细节写错wa了一发
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int num[1000];
int main() {
int T; scanf("%d", &T);
while(T--) {
int n, k;
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i) scanf("%d", &num[i]);
int pos = 1;
while(1) {
if(pos != n) {
if(k > num[pos]) num[n] += num[pos], k -= num[pos], num[pos] = 0, pos++;
else if(k == num[pos]) {
num[n] += num[pos], k -= num[pos], num[pos] = 0;
break;
}
else if(k < num[pos]) {
num[pos] -= k, num[n] += k, k = 0;
break;
}
}
else break;
}
for(int i = 1; i <= n; ++i) printf("%d ", num[i]);
printf("\n");
}
return 0;
}
B. AGAGA XOOORRR
题意:一串数字,每次可以让相邻两个数异或合并,问最后能不能让所有数一样并且剩下的串长度至少为2。
一看异或就头大,反正肯定又是个结论题
一开始想拆位dp发现不太会搞,然后我们发现最终答案序列长度一定可以缩短到2或者3(假设有n个相同的2,那么可以把2个合成0,然后0异或别的都是其本身,也就是说可以一直缩短)。
假设长度能缩到2,那两两分组互相异或都是0,因此原序列如果异或和为0一定是合法的(简谐证明我不会,可以暴力证
假设长度能缩到3,那两两分组完剩下那个数字就是全部相同的那个数字,这个数字的大小就是所有数的异或和,因此我们只要能保证原序列能搞出>=2个这个数字就行,从前往后跑异或前缀和每次和总异或和相同就+1。
#include <cstdio>
#include <iostream>
#include <cstring>
#define LL long long
using namespace std;
LL num[4000];
int main() {
int T; scanf("%d", &T);
while(T--) {
int n; LL pre = 0;
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%lld", &num[i]);
pre ^= num[i];
}
if(!pre) puts("YES");
else {
LL temp = 0, cnt = 0;
for(int i = 1; i <= n; ++i) {
temp ^= num[i];
if(temp == pre) {
temp = 0;
++cnt;
}
}
if(!temp && cnt >= 2) puts("YES");
else puts("NO");
}
}
return 0;
}
C. Baby Ehab Partitions Again
题意:给一串数字,问你最少删几个数能保证无论怎么把操作完的这串数分成两个子序列,两段的数字和都不一样。
首先数字总和如果是奇数,那随便你分都不可能一样,所以奇数可以直接特判输出0。
完事之后就是经典邮票问题了,dp判断能不能有数字能凑出来sum/2,如果可以踹走一个奇数就行。
但如果原序列一个奇数都没有,我们发现对这个序列同时除以2是不影响答案的,因此我们一直除一直除直到有一个数变成奇数,踹走这个数就行。
因此我们证明了如果要删那最多删一个,删的那个一定是质因数分解后2次幂最低的那个数,因此我们读入的时候把这个数搞出来就行了。
因为sum比较大所以得滚动数组,倒着来。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int sum;
int num[4000];
bool dp[400000];
int main() {
int n;
int min_pow = 0x3f3f3f3f;
int del_pos;
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &num[i]);
sum += num[i];
int cnt = 0, temp = num[i];
while(temp % 2 == 0) {
temp /= 2;
cnt++;
}
if(cnt < min_pow) {
min_pow = cnt;
del_pos = i;
}
}
if(sum % 2) puts("0");
else {
dp[0] = true;
for(int i = 1; i <= n; ++i) {
for(int j = sum + 10; j >= 0; --j) {
if(dp[j]) dp[j + num[i]] = true;
}
}
if(!dp[sum / 2]) puts("0");
else printf("1\n%d\n", del_pos);
}
return 0;
}
D. Cut
题意:给一串数,q个询问,每个询问给lr,问最少把[l,r]分几段能够使得每段的乘积=他们的LCM。
显然乘积=LCM当且仅当所有数字全部互素,看一下nq范围显然每次跑个最大团不大行,所以考虑如何优化复杂度。