BZOJ4693 雪中送温暖

传送门

题目大意

定义$K$维信号灯$(K\leq 9)$有$K$个非负整数$X_1,X_2...X_k$组成。

定义一个信号灯的$K$个前驱信号灯由$K$个$X$分别$-1$得到。

当$\prod\limits_{i=1}^K X_i=0$时信号灯为绿色,当$X_1=X_2=...=X_K=1$时信号灯为红色。

若一个信号灯的$K$个前驱中有偶数个是红色,则该信号灯是绿色,否则是红色。

给定$K$组区间$[L_i,R_i]$,求满足$X_i\in[L_i,R_i]$的红色信号灯的数量$(1\leq L_i\leq R_i\leq 10^{15})$。

 

题解

思考一下这个递推的过程实际上是在干什么

想像一个信号灯是在$K$为空间的一个坐标,初始在$X_i=1$,每次可以选择一维坐标$+1$,红色表示能到达这个坐标的方案为奇数,绿色表示到达这个坐标的方案为偶数。坐标必须为正,所以出现$X_i=0$表示这个坐标不可被到达,方案数为$0$,那么初始时在$\{X_i\}$,方案数为$1$,考虑如何计算到达每个点的坐标的方案数。

我们令$X_i=X_i-1$,这样就求出了每个维度坐标需要增加的数量,红绿情况就是

$$\prod\limits_{i=1}^{K}\binom{\sum\limits_{j=1}^{i}X_j}{X_i}(\mod\space 2)$$

用卢卡斯定理大力推一波得到出在二进制意义下$X_i$之间的加法没有进位。

即$X_i$异或和对于$\sum X_i$,二进制下每一位上在所有的$X_i$至多只出现一个$1$。

然后就可以数位$DP$了,将$K$个数卡位情况压成两个二进制状态。

$F_{(i,s_1,s_2)}$表示对于前$i$位,下界被卡住情况为$s_1$,上界被卡住情况为$s_2$的方案数。

枚举第$i$位不填或填在某一个$X_i$上,结合对卡位情况的改变进行转移。

其实用意义的状态很少,复杂度大概是$O(T(2^{2K}+K3^K)\log_2 R_i)$。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 52
#define mod 998244353
using namespace std;
LL read(){
	LL nm=0,fh=1; int cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10ll+(cw-'0');
	return nm*fh;
}
LL L[M],R[M]; 
int K,m,F[M][540][540],MAXN,vis[11],ans;
int add(int x,int y){return (x+y)>=mod?x+y-mod:x+y;}
void upd(int &x,int y){x=add(x,y);}
void write(int x){if(x>9)write(x/10);putchar(x%10+'0');}
int main(){
	for(int T=read();T;--T,write(ans),putchar('\n')){
		K=read(),m=ans=0,memset(F,0,sizeof(F)),MAXN=(1<<K)-1;
		for(int i=0;i<K;i++) L[i]=read()-1;
		for(int i=0;i<K;i++){
			R[i]=read()-1;
			while((1ll<<m)<=R[i]) m++;
		} F[m][MAXN][MAXN]=1;
		for(int i=m;i>=0;i--){
			for(int s1=0;s1<=MAXN;s1++){
				for(int s2=0;s2<=MAXN;s2++){
					if(!F[i][s1][s2]) continue; if(!i){upd(ans,F[i][s1][s2]);continue;}
					int v=i-1,ct=0,t1=s1,t2=s2,ot; memset(vis,0,sizeof(vis));
					for(int k=0;k<K;k++){
						if((s1>>k)&(L[k]>>v)&1) ct++,vis[k]=1;
						if((s2>>k)&1){if(!((R[k]>>v)&1)) vis[k]=2;else t2^=(1<<k);}
					}
					if(ct>1) continue; if(!ct) upd(F[i-1][s1][t2],F[i][s1][s2]);
					for(int k=0;k<K;k++){
						if(!(vis[k]==1||(!vis[k]&&!ct))) continue;
						t1=((s1>>k)&((L[k]>>v)^1)&1)?s1^(1<<k):s1;
						ot=((s2>>k)&(R[k]>>v)&1)?t2^(1<<k):t2;
						upd(F[i-1][t1][ot],F[i][s1][s2]);
					}
				}
			}
		}
	}
	return 0;
}
posted @ 2018-10-08 19:59  OYJason  阅读(265)  评论(0编辑  收藏  举报