求生之路

题目大意

给出n个数a[1]~a[n]
求有多少个d满足
an0ni=1an=(an xor (a[i]d)),an0
数据范围,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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值