UniversalOJ 681 月球列车
这里提供一种可以被hack成\(O(n\log^2_2{n})\)但在随机数据下呈现\(O(n\log_2{n})\)的做法。最大时间点是1782ms(相比于3s时限来说很少了),总耗时9918ms,在180份AC代码中排名是第58,可以看见还是非常优秀的。
说道这个假做法,我们先来考虑一个60pts的做法,我们分类讨论每一位的结果,一个位置是否有\(1\)仅跟\(a_i\)这一位上是否有\(1\),\(x\)这一位上是否有\(1\),\(a_i+x\)是否会在\(i-1\)这一位上产生进位。
我们以\(x\)这一位上有\(1\)举例。进行以下\(3\)步转化。
1.\(a_i\)上这一位没有\(1\)且产生进位的个数和\(a_i\)上这一位有\(1\)且不产生进位的个数。(直接定义)
2.\(a_i\)上这一位没有\(1\)的个数减去\(a_i\)上这一位没有\(1\)且不产生进位的个数和\(a_i\)上这一位有\(1\)且不产生进位的个数。(转化含义。)
3.\(a_i\)上这一位没有\(1\)的个数和\(a_i\)上这一位没有\(1\)且不产生进位的个数和\(a_i\)上这一位有\(1\)且不产生进位的个数。(一个值加上或减去另一个相同的值奇偶性不变。)
4.不产生进位的个数的个数和\(a_i\)上这一位没有\(1\)的个数。(组合一下。)
\(x\)上这一位没有\(1\)同理,然后我们只需要统计产生进位的个数,假设是要\(a_i+x\)进给第\(j\)位,那么我们容易发现这等价于\((a_i \wedge (2^j-1))+(x \wedge (2^j-1)) \ge 2^j\),我们可以统计\(a_i\)的序列并且排序,然后直接二分答案就可以做到\(O(n\log_2{n})\)了,可以拿到60分。
可是正解我看不懂怎么办,不要急,我们考虑优化我们的暴力,为什么会\(TLE\)呢,究其原因是因为我们二分了,可是如果省去二分来跑答案就有可能导致答案错误(或者让你的算法变得更加高级,以至于成为正解),我们考虑在\(j \le 15\)这些位置的时候开一个桶来统计答案,在\(j > 15\)的时候,我们先提前统计出来在\(j\)以下\(15\)位的答案,并且统计出来\(j\)在\(15\)以后的影响区间。(人话就是离线下来统计出\(x>>(j-15)\)的答案,然后再统计出可以影响到\(x>>(j-15)\)到\(x>>(j-15)+(1<<(j-15))-1\)这一段区间的\(a_i\)区间)然候对于这一段区间进行暴力枚举答案就可以了,分析一下复杂度,我们会发现\(2^{15}=32768\),相当于我们把\(n\)个数划分成了\(32768\)个区间,平均下来每一个区间只有不到\(1\)个数,那么这个二分的复杂度就可以忽略不计了。
题外话:
其实如果真的有人想要卡你的话,你甚至设置一个函数分区间,只需要满足分成了3w组区间且每一段区间连续就可以了。(这是一个同学提供给我的思路,但是我暂时没有想到很好的函数来划分区间)
但是这给了你很大的发挥空间,理论上来说你可以用位运算乱搞达成想卡你的人都不能准确算出你的区间,或者说拿随机数来划分区间,如果被卡了稍微改改函数就可以了。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=250010;
const int K=15;
int n,m,t,xi,ge[65][2],cur,id[2][maxn],gg,tp,cc[16][33000],cha[70][33000],qi[70][33000],luo[70][33000],l,r;
long long x,a[maxn],lst,rt[65][maxn],lastans,ll,zg;
int query(int id,int q,int w){
if(q>w){
return 0;
}
l=q;
r=w;
while(l<=r){
int mid=(l+r)/2;
if(rt[id][mid]+lst>=(1ll<<id)){
r=mid-1;
}
else{
l=mid+1;
}
}
return w-l+1;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>t;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
id[1][i]=i;
}
cur=1;
for(int i=1;i<=60;i++){
lst=cur;
cur^=1;
gg=0;
for(int j=1;j<=n;j++){
if(!(a[id[lst][j]]&(1ll<<(i-1)))){
gg++;
rt[i][gg]=rt[i-1][j];
id[cur][gg]=id[lst][j];
}
}
ge[i-1][0]=gg;
ge[i-1][1]=n-gg;
for(int j=1;j<=n;j++){
if(a[id[lst][j]]&(1ll<<(i-1))){
gg++;
rt[i][gg]=rt[i-1][j]+(1ll<<(i-1));
id[cur][gg]=id[lst][j];
}
}
}
for(int i=0;i<=K;i++){
tp=n+1;
for(int j=0;j<(1ll<<i);j++){
while(tp&&rt[i][tp-1]+j>=(1ll<<i)){
tp--;
}
cc[i][j]=n-tp+1;
}
}
for(int i=K+1;i<=60;i++){
tp=n+1;
zg=1ll<<(i-K);
for(long long j=0;j<(1<<K);j++){
while(tp&&rt[i][tp-1]+(j<<(i-K))>=(1ll<<i)){
tp--;
}
cha[i][j]=n-tp+1;
qi[i][j]=tp;
luo[i][j]=tp-1;
while(tp&&rt[i][tp-1]+(j<<(i-K))+zg-1>=(1ll<<i)){
tp--;
qi[i][j]=tp;
}
}
}
ge[60][0]=n;
while(m--){
cin>>x;
x=x^(t*(lastans>>20));
lst=0;
lastans=0;
for(int i=0;i<=K;i++){
xi=cc[i][lst];
if(x&(1ll<<i)){
xi+=ge[i][0];
lst+=(1ll<<i);
}
else{
xi+=ge[i][1];
}
if(xi&1){
lastans+=(1ll<<i);
}
}
for(int i=K+1;i<=60;i++){
ll=lst>>(i-K);
xi=cha[i][ll]+query(i,qi[i][ll],luo[i][ll]);
if(x&(1ll<<i)){
xi+=ge[i][0];
lst+=(1ll<<i);
}
else{
xi+=ge[i][1];
}
if(xi&1){
lastans+=(1ll<<i);
}
}
cout<<lastans<<'\n';
}
return 0;
}
浙公网安备 33010602011771号