[UR#22] 月球列车

一、题目

点此看题

这种比较精细的题还是要多练练,其实不怎么难但是我看题解都看了三个小时

二、解法

位运算和四则运算混合在一起是很恶心的,方法基本上只有按位考虑。

对于数位 \(w\),我们只需要考虑 \(a_i/x\) 在数位 \(w\) 出现次数的奇偶性,和考虑 \(a_i+x\)\(w-1\) 这个数位上进位次数的奇偶性即可,前者特别容易计算,难点是后者。

一定要记住这种进位问题是有单调性的,我们把 \(a_i\) 按前 \(w-1\) 位从小到大排序后,能产生进位的一定是一段后缀,设 \(V=2^w-x\and(2^w-1)\),进位的充要条件是 \(a_i\and(2^w-1)\geq V\)

如果暴力预处理排序、查询时候暴力二分的话时间复杂度 \(O(n\log n\log a)\)

考虑优化,因为二进制的特性我们可以在排序的时候使用归并排序。查询时候我们想快速得到 \(V\) 在数组上的位置,可以预处理 \(c[w][i][0/1]\) 表示以前第 \(i\) 个位置上的数,考虑第 \(w\) 位之后,不加 \(2^w/\)加上 \(2^w\) 在新数组中的位置。

假设我们知道 \(V\) 以前在数组中的位置,可以通过跳 \(c\) 数组来处理出 \(V\) 在数组中的新位置,这样求出来的是小于等于 \(V\) 的最后一个位置。对于计算答案,如果 \(x\) 这一位为 \(0\) 那么我们找在 \([V,V+2^w)\) 中的数个数,因为 \(a_i\geq V+2^w\) 会因为进位和原本这一位就有值贡献被抵消了;如果 \(x\) 这一位为 \(1\) 那么贡献的数全部取反即可。

时间复杂度 \(O(n\log a)\)真的是道签到题呢

#include <cstdio>
#include <cstring>
const int M = 250005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,t,a[M],c[64][M][2],tmp[M],cur[M];
int work(int x)
{
	int cur=n,ans=0;
	for(int w=0;w<=60;w++)
	{
		int cnt=c[w][cur][1]-c[w][cur][0];
		if(!((x>>w)&1)) cur=c[w][cur][1];
		else cnt=n-cnt,cur=c[w][cur][0];
		if(cnt&1) ans|=(1ll<<w);
	}
	return ans;
}
signed main()
{
	n=read();m=read();t=read();
	for(int i=1;i<=n;i++)
		a[i]=read(),cur[i]=i;
	for(int w=0;w<=60;w++)
	{
		int cnt=0;
		c[w][0][0]=cnt;
		for(int i=1;i<=n;i++)
		{
			if(!((a[cur[i]]>>w)&1)) tmp[++cnt]=cur[i];
			c[w][i][0]=cnt;
		}
		c[w][0][1]=cnt;
		for(int i=1;i<=n;i++)
		{
			if((a[cur[i]]>>w)&1) tmp[++cnt]=cur[i];
			c[w][i][1]=cnt;
		}
		memcpy(cur,tmp,sizeof cur);
	}
	int ans=0;
	for(int i=1;i<=m;i++)
	{
		int x=read();
		if(t) x^=(ans>>20);
		ans=work(x);
		printf("%lld\n",ans);
	}
} 
posted @ 2021-10-07 15:09  C202044zxy  阅读(277)  评论(0编辑  收藏  举报