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; }