Codeforces Round #717 Div.2

凌晨睡不着 练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范围显然每次跑个最大团不大行,所以考虑如何优化复杂度。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值