icpc 徐州 A-cat规律

17 篇文章 0 订阅
2 篇文章 0 订阅

A-cat vj链接
题意:
T次测试 ( T ≤ 5 × 1 0 5 ) (T\le5\times10^5) T5×105,每次给定 L 、 R L、R LR区间以及数字 S S S,要求在这个区间中找到最长子段 [ x , y ] [x,y] [x,y],使得 x ⨁ ( x + 1 ) ⨁ . . . ⨁ ( y − 1 ) ⨁ y ≤ S x\bigoplus (x+1)\bigoplus...\bigoplus(y-1)\bigoplus y\le S x(x+1)...(y1)yS成立。
数据范围: 1 ≤ L ≤ R ≤ 1 0 18 , 1 ≤ S ≤ 2 × 1 0 18 1\le L\le R\le10^{18},1\le S \le 2\times10^{18} 1LR1018,1S2×1018

思路:
找规律,打表出来的图是这样的。每次固定开头,固定结尾挺难看出来的。注意每次0出现的位置。
这是偶数4开头的数字:
在这里插入图片描述
这是偶数2开头的数字:
在这里插入图片描述
但是奇数开头没有这个规律。
这是奇数3开头的数字:
在这里插入图片描述

可以发现一个规律偶数开头的,区间长度是4的倍数出现一次0
那么问题就可以转换成贪心先把所有偶数开头的,区间长度为4的小区间合并,他们的答案是最小的0。
这样操作之后区间内可能剩下的有两类数字:

  1. 开头的一个奇数
  2. 尾部的余数

剩下的数字对于答案的拓展作用只在于: 0 ⨁ ? 0\bigoplus? 0之后的答案 ≤ S \le S S,那么最后答案可以加上 ? ? 的长度。
对他们进行前缀和,每次进行枚举前后端点,判断是否是合法的解,维护最大值。
由于这个区间的长度范围 ≤ 4 \le4 4,所以最后的时间复杂度是 O ( T ∗ 16 ) O(T*16) OT16

注意点:

  1. 因为数据范围是 1 0 18 10^{18} 1018级别的,cin会t,用scanf
  2. 判断时候异或操作^要加括号!!!!!!!

AC代码:

LL xo(LL x) {
	LL tt = x, pos = 1, one, zero;
	x++;
	LL ans = 0;
	while (tt) {
		zero = x / (1ll << pos)*(1ll << (pos - 1)) + min(x % (1ll << pos), 1ll << (pos - 1));
		one = x - zero;
		if (one % 2)ans += 1ll << (pos - 1);
		tt >>= 1;
		pos++;
	}
	return ans;
}
LL dp[110], a[110];
int main(){
	int t, nw;
	LL l, r, s, ans;
	l = 0;
	/*for (LL i = 0; i <= 10; i++) {
		for (LL j = i + 1; j <= 20; j++) {
			ans = xo(i) ^ xo(j);
			cout << "[" << i + 1 << "," << j << "] " << ans << endl;
		}
	}*///偶数开头的,区间长度是4的倍数出现一次0
	cin >> t;
	while (t--) {
		nw = 0;
		scanf("%lld%lld%lld", &l, &r, &s);
		//cin >> l >> r >> s;
		ans = 0;
		a[0] = 0; dp[0] = 0;
		if (l % 2)a[++nw] = l, dp[nw] = dp[nw - 1] ^ a[nw], l++;
		ans += (r - l + 1) / 4 * 4;
		/*a[++nw] = 0;
		dp[nw] = dp[nw - 1] ^ a[nw];*/
		if ((r - l + 1) % 4) {
			for (LL i = l + (r - l + 1) / 4 * 4; i <= r; i++) {
				a[++nw] = i, dp[nw] = dp[nw - 1] ^ a[nw];
			}
		}
		LL tt = ans;
		for (int i = 0; i <= nw; i++) {
			for (int j = i + 1; j <= nw; j++) {
				if ((dp[j] ^ dp[i]) <= s)ans = max(ans, tt + (j - i));
			}
		}
		if (ans)printf("%lld\n", ans);
		else printf("-1\n");
		/*if (ans)cout << ans << endl;
		else cout << -1 << endl;*/
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值