版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/83109751
Description
历经千辛万苦,pty终于打开了金字塔的锁。稍稍适应了外面刺眼的光线,pty抬头望去,眼前竟是一条不见尽头的狭长通道。这时候背后响起了奇怪的窸窣声,原来是金字塔内绿眼黑身的怪物追了过来。Pty来不及多想,便拼命往前奔去。通道狭窄又曲折,时不时还有断裂,不过Pty凭借TempleRun练成的娴熟技巧轻松通过。眼看着离怪物们越来越远时,一棵参天大树突然耸立在了道路中央,大树摆了摆身子,用苍老的声音说道:“孩子,我是远古的守护神。你打扰了这里的清净,想要从我这里通过,必须要解决一道来自远古的问题。”
现在有n堆石子,每堆石子分别有ai个,问有多少个d使得下式成立:
守护神出的题自然是神题了,同是身为神的你可以解决么?
Solution
由于每一位异或是独立的,我们明显应该考虑数位DP。
对于每一位分0/1讨论,再考虑上退位的情况。
但摆在眼前的困难是,我们既不能记已经减去的d为状态(状态数是ai的),也不能记有哪些是需要退位的(状态数为 )
但有一个性质非常显然而又容易忽视。
假设我们当前做到第i位(
),有j个数需要退位,那么这j个数一定是所有数中
位到
位最小的j个(只考虑末i位,减去同样的d,更小的一定更有可能退位)
那么对于每一位,我们都预处理只考虑末这么多位排序的结果。
状态就可以精简成
的:
表示当前做到
这一位,有j个数需要退位。
此时只需要分0/1讨论一下,统计一下1的个数是否为偶数即可。
复杂度
Code
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
#define LL long long
using namespace std;
int n,a1[62][N],cnt[62];
LL f[62][N],a[N],cf[61];
int main()
{
cin>>n;
fo(i,1,n) scanf("%lld",&a[i]),a1[0][i]=i;
cf[0]=1;
fo(i,1,60) cf[i]=cf[i-1]*(LL)2;
fo(j,1,60)//方便起见这里的j=0实际上是第-1位,j=1才是2^0
{
int t0=0;
fo(i,1,n) if(!(a[i]&cf[j-1])) t0++;
fo(i,1,n)
{
if(a[a1[j-1][i]]&cf[j-1]) a1[j][++t0]=a1[j-1][i];
else a1[j][++cnt[j]]=a1[j-1][i];
}
}
//上面部分为排序,记录cnt[j]表示2^(j-1)这一位为0的a的个数
f[0][0]=1;
fo(j,0,59)
{
int w=0,w1=0;
fo(i,0,n)
{
if(i)
{
if(a[a1[j][i]]&cf[j]) w1++;
else w++;
}
if((n-cnt[j+1]-w1+w)%2==0) f[j+1][w]+=f[j][i];//讨论2^j这一位选0
if((w1+cnt[j+1]-w)%2==0) f[j+1][cnt[j+1]+w1]+=f[j][i];//讨论2^j这一位选1
}
}
LL mi=1e18,v=0;
fo(i,1,n) mi=min(mi,a[i]);
fo(i,1,n) v^=(a[i]-mi);
if(v==0) v=-1;
else v=0;
printf("%lld\n",f[60][0]+v);
}