题目大意
给出n个数a[1]~a[n]
求有多少个d满足
an初始值为0,∑ni=1an=(an
xor
(a[i]−d)),an最终值为0
数据范围,n<=2*10^5,a[i]<=10^18。
数位DP
由于有xor操作,我们容易想到拆位,进而想到数位dp。
设f[i][j]表示,当前做到第i位,有j个数当前位要退位。
因为退位会是低位影响高位,所以我们要从低位开始dp。
有一个显然的结论
当有j个数要退位时,一定是序列中从第i位开始记值情况下的前j小,既然显然我就不解释了。
所以每次我们从第i位开始记值情况下对a[i]进行排序,每次可以利用i-1位的排序结果来排序,O(n)解决。
每次转移是O(1)的,通过在d的第i位放0或放1可以计算下一位有多少个要退位。于是总时间为O(n log n)。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=200000+5;
ll mina=1000000000000000000,n,f[61][maxn],p[maxn];
int a[61][maxn],b[61][maxn],c[61],m;
int read(ll &n){
char ch=getchar();
while((ch<'0')||(ch>'9'))ch=getchar();n=0;
while(ch>='0' && ch<='9')n=n*10+ch-'0',ch=getchar();
}
int main(){
read(n);
for (int i=1;i<=n;i++) {
ll x;read(x);p[i]=x;a[0][i]=i;
mina=min(mina,x);int j=0;
while (x>0) {
b[j][i]=x%2;c[j++]+=x%2;
x/=2;
}m=max(m,j-1);
}f[0][0]=1;
for (int i=0;i<=m;i++){
int s=c[i],s1=0,s2=n-c[i];
for (int j=0;j<=n;j++) {
if (j>0) if (b[i][a[i][j]]==1) s--,s1++;else s++,s2--;
if (f[i][j]>0) {
if (s%2==0) f[i+1][j-s1]+=f[i][j];
if ((n-s)%2==0) f[i+1][j+s2]+=f[i][j];
}
}
int z=1,z1=(n-c[i])+1;
for (int j=1;j<=n;j++){
int x=a[i][j];
if (b[i][x]==0) a[i+1][z++]=x;else
a[i+1][z1++]=x;
}
}
int s=0;
for (int i=1;i<=n;i++) s=s xor (p[i]-mina);
if (s==0) f[m+1][0]--;
printf("%lld",f[m+1][0]);
}