洛谷 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;
}
posted @ 2026-01-10 19:51  yemuzhe  阅读(2)  评论(0)    收藏  举报