洛谷 P3447 [POI 2006] KRY-Crystals
一个比较自然的想法是从高往低数位 dp,但要记 \(n\) 个 \(0/1\) 就爆了。
考虑 \(a _ n = 2 ^ {32} - 1\) 的情况,那么前面的 \(a _ i\) 可以随便填,因为异或和 \(= 0\) 所以 \(a _ n\) 是唯一确定的,方案数为 \(\prod _ {i = 1} ^ {n - 1} (a _ i + 1)\)。
所以数位 dp 过程中,一旦这 \(n\) 个 \(0 / 1\) 状态中存在 \(0\),那么就可以直接通过上面方法算出方案数,总体上做一个简单的 dp 即可求出;到下一位时只用考虑 \(0 / 1\) 状态全是 \(1\),也就是前面的位都顶满的情况。
时间复杂度 \(\text O (n \log V)\)。
#include<cstdio>
#include<cstring>
#define N 55
using namespace std;
typedef unsigned uint;
typedef unsigned long long ull;
const int m=32;
int n;
uint a[N];
ull ans,g[N][2];
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%u",&a[i]);
for(int i=m-1;~i;i--) {
memset(g,0,sizeof(g)),g[0][0]=1;
int c=0,cc=0; ull res=0;
for(int j=1;j<=n;j++) c+=a[j]>>i&1;
for(int j=1;j<=n;j++) {
res*=((a[j]&(1u<<i)-1)+1);
if(a[j]>>i&1) {
cc++,res+=g[j-1][(c-cc)&1];
}
for(int k=0;k<2;k++) {
if(a[j]>>i&1) {
g[j][k]+=g[j-1][k]*(1u<<i);
g[j][k]+=g[j-1][!k]*((a[j]&(1u<<i)-1)+1);
} else {
g[j][k]+=g[j-1][k]*((a[j]&(1u<<i)-1)+1);
}
}
}
ans+=res;
if(c&1) {ans--; break;}
}
printf("%llu\n",ans);
return 0;
}

浙公网安备 33010602011771号