1. 程式人生 > >bzoj 4693 雪中送溫暖 - 組合數學 - 盧卡斯定理 - 數位dp

bzoj 4693 雪中送溫暖 - 組合數學 - 盧卡斯定理 - 數位dp

題解:
考慮本質上是在對超矩形的每個點求從(1,1,…,1)走到這個點的方案數的奇偶性之和。
( x 1 , x 2 ,

, x k ) (x_1,x_2,\dots,x_k) 的方案數顯然為(所有 x
i x_i
都要-1):
( i
= 1 k x i ) ! x i ! \frac{\left(\sum_{i=1}^k x_{i}\right)!}{\prod x_i!}

直接這麼做是沒有前途的,考慮更原始的式子:
i = 1 k ( j = i k x j x i ) \sum_{i=1}^k\binom{\sum_{j=i}^k x_j}{x_i}
我們顯然可以任意交換 x i x_i 的順序,因此這個式子是奇數,一個必要條件是:
i ̸ = j , ( x i + x j x i ) = 1   ( m o d   2 ) ( x i + x j ) & x i = 0 , x i & x j = 0 \forall i\not=j,\binom{x_i+x_j}{x_i}=1\ (\mathrm{mod}\ 2)\\ (x_i+x_j)\&x_i=0,x_i\&x_j=0
很快就會意識到這個條件是充分的,因為若任意兩數二進位制交集為空,那麼做加法和做或運算沒有區別,因此式子有值。
因此結論是:方案數模2有值,當且僅當 j , i = 1 k x i [ j ] 1 \forall j,\sum_{i=1}^kx_i[j]\le1 ,也就是每一位至多一個1,這個直接數位dp一下既可以了。
最終關於L可以容斥一下,複雜度 O ( 2 2 k k log R ) O\left(2^{2k}k\log R\right)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
#define mod 998244353
#define lint long long
#define N 11
#define LEN 60
using namespace std;
inline int upd(int &x,int y) { return (x+=y)>=mod?x-=mod:0; }
lint up[N],L[N],R[N];int xs[1<<N],a[N][LEN],dp[LEN][1<<N];
inline int getlst(lint x,int *a)
{
	int n=0;memset(a,0,sizeof(int)*(LEN));if(!x) return 1;
	while(x) a[++n]=x&1,x>>=1;return n;
}
inline int calc(int k)
{
	int n=0,all=1<<k,v;rep(i,0,k) if(up[i]<0) return 0;
	rep(i,0,k) n=max(n,getlst(up[i],a[i]));
	rep(i,0,n+1) memset(dp[i],0,sizeof(int)*all);dp[0][all-1]=1;
	rep(i,0,k) for(int j=1;j<=n/2;j++) swap(a[i][j],a[i][n-j+1]);
	rep(i,0,n) rep(s,0,all) if((v=dp[i][s]))
	{
		int t=s;
		rep(j,0,k) if(a[j][i+1]&&((s>>j)&1)) t^=1<<j;
		upd(dp[i+1][t],v);
		rep(j,0,k)
			if(!((s>>j)&1)) upd(dp[i+1][t],v);
			else if(a[j][i+1]) upd(dp[i+1][t^(1<<j)],v);
	}
	lint ans=0;rep(s,0,all) ans+=dp[n][s];return (int)(ans%mod);
}
int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		int k,all;scanf("%d",&k),all=1<<k;lint ans=0ll;
		rep(i,0,k) scanf("%lld",&L[i]),L[i]--;
		rep(i,0,k) scanf("%lld",&R[i]),R[i]--;
		xs[0]=1;rep(i,1,all) xs[i]=-xs[i^(i&-i)];
		for(int i=0;i<all;ans+=xs[i]*calc(k),i++)
			rep(j,0,k) up[j]=(((i>>j)&1)?L[j]-1:R[j]);
		ans%=mod,ans+=mod,ans%=mod,printf("%lld\n",ans);
	}
	return 0;
}