[UR#22]月球列车

题目

传送门 to UOJ

思路

妹妹说:“其实不怎么难,真的是道签到题。” 替代理论冒头了,母系社会正在崛起!

首先拆位。记 v w v_w vw a i a_i ai 的按位取反值的前 w w w 位(即删去 2 w 2^w 2w 及高位),同理可记 x w x_w xw,显然 ( a i + x ) (a_i{+}x) (ai+x) 2 w 2^w 2w 这一位上,在 x w + 1 ∈ ( v w ,    v w + 2 w ] x_{w+1}\in(v_w,\;v_w{+}2^w] xw+1(vw,vw+2w] 时是 0 / 1 0/1 0/1(会进位),其余时候则是 1 / 0 1/0 1/0(不进位)。所以答案就是在 a n s ∣ x = 0 = ( ⨁ a i ) ans\Big|_{x=0}=(\bigoplus a_i) ans x=0=(ai) 的基础上,再对每个 w w w 询问有多少个 v w v_w vw ( v w + 2 w ) (v_w{+}2^w) (vw+2w) 小于 x w + 1 x_{w+1} xw+1,若为奇数个,则需异或 2 w 2^w 2w

直接取出分界点 v w v_w vw ( v w + 2 w ) (v_w{+}2^w) (vw+2w),询问时二分查找,复杂度 O ( n log ⁡ a log ⁡ n ) \mathcal O(n\log a\log n) O(nlogalogn),直接 T \textit T T 飞。

怎样优化?发现分界点 v v v 是若干后缀。我就开始考虑从低位向高位插入的 t r i e \tt trie trie 了,然后人傻了。我甚至猜测 ⌊ lastans 2 20 ⌋ \lfloor{\text{lastans}\over 2^{20}}\rfloor 220lastans 有无深意。

应注意到,比较 x w + 1 x_{w+1} xw+1 v w v_w vw 的大小时,要么比较最高位,要么比较后面的位。而后面的位恰好是 v w − 1 v_{w-1} vw1,已经比较过。这是类似 基数排序 的思想,即先比较较低位,确定(只看较低位时的)相对排名,然后再将较高位纳入考虑。

那么至少所有分界点是可以 O ( n log ⁡ a ) \mathcal O(n\log a) O(nloga) 排序的——本质上就是对 a i a_i ai 的按位取反结果做低位到高位的基数排序,过程中得到的排名。

查询的时候,相当于是已知低位的 rank \text{rank} rank,所以预处理 f w ( i , 0 / 1 ) f_w(i,0/1) fw(i,0/1) 表示 x w x_{w} xw { v w } \{v_w\} {vw} 中的 rank \text{rank} rank i i i 时, x w + 1 = x w + ( 0 / 1 ) × 2 w x_{w+1}=x_w+(0/1)\times 2^w xw+1=xw+(0/1)×2w { v w + 1 } \{v_{w+1}\} {vw+1} 中的 rank \text{rank} rank 。这可以在基排的同时轻松求出。

时间复杂度 O ( n log ⁡ a ) \mathcal O(n\log a) O(nloga),真的是道让我成为脑瘫的题呢。我直接 Shaya 化

代码

统计答案时,由于实际是求 x w + 1 x_{w+1} xw+1 { v w ,    v w + 2 w } \{v_w,\;v_w{+}2^w\} {vw,vw+2w} 中的排名,浅分类讨论即可。

#include <bits/stdc++.h>
// OUYE the God is watching us.
using llong = long long;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
	for(; isdigit(c); c=getchar()) a = a*10+(c^48);
	return a*f;
}
inline llong readlong(){
	llong a = 0; int c = getchar();
	for(; !isdigit(c); c=getchar());
	for(; isdigit(c); c=getchar()) a = a*10+(c^48);
	return a; // no negative support
}
inline void writelong(const llong &x){
	if(x > 9) writelong(x/10);
	putchar(int(x%10)^48);
}

const int LOGA = 61, MAXN = 250000;
llong a[MAXN+1], b[MAXN+1];
int dp[LOGA][MAXN+1][2];

int main(){
	int n = readint(), q = readint(), opt = readint();
	llong base = 0;
	rep(i,1,n){
		a[i] = readlong();
		base ^= a[i], a[i] = ~a[i];
	}
	for(int w=0; w!=LOGA; ++w){
		int rnk = dp[w][0][0] = 0;
		rep(i,1,n) rnk += !int(a[i]>>w&1), dp[w][i][0] = rnk;
		int ptr[2] = {0,rnk}; dp[w][0][1] = rnk;
		rep(i,1,n) rnk += int(a[i]>>w&1), dp[w][i][1] = rnk;
		rep(i,1,n) b[++ ptr[a[i]>>w&1]] = a[i];
		memcpy(a+1,b+1,n<<3);
	}
	for(llong x,lst=0; q; --q){
		x = readlong(); if(opt) x ^= (lst>>20);
		lst = base; int rnk = 0;
		rep0(i,0,LOGA){
			if(x>>i&1){
				if((rnk^n)&1) // x-2^w > v and x > v
					lst ^= (1ll<<i);
				rnk = dp[i][rnk][1];
			}
			else{
				if(rnk&1) lst ^= (1ll<<i);
				rnk = dp[i][rnk][0];
			}
		}
		writelong(lst), putchar('\n');
	}
	return 0;
}

在这里悄悄向校长检讨自己的过错 =(

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值