[2019CCPC哈爾濱] B Binary Numbers dp

題意比較複雜。
給出m17m\leq17 表示我現在有[0,2m1][0,2^{m}-1]這樣的一個區間,然後現在有0N2m0\leq N \leq 2^{m}表示我將會把這個區間分爲NN段區間,這NN段是覆蓋所有的區間並且嚴格不相交的。
然後在每個區間裏面都選一個數Ai[Li,Ri]A_{i}\in[L_{i},R_{i}],然後要滿足對於任意一個區間,都有AiA_{i}和這個區間中任何數的二進制表示的前綴最大值不小於AjA_{j}和這個區間中任何數的二進制表示的前綴最大值。
如果存在滿足這樣的一個序列,它的貢獻是i=1NAi\prod_{i=1}^{N}A_{i}。求所有方案的貢獻之和,答案模1e8+71e8+7
其實冷靜思考一下,這個前綴最大值越大,說明這個數和當前的數越接近。
那麼對於任何一個區間裏面的數,我只要保證它和離它最遠的兩個端點都比旁邊的數和端點的值就可以了。
記錄lcp(x,y)lcp(x,y)表示xxyy的前綴最大值。那麼限制條件爲:
lcp(ai,li)lcp(ai1,li)lcp(a_i,l_i)\geq lcp(a_{i-1},l_i)
lcp(ai,ri)lcp(ai+1,ri)lcp(a_i,r_i)\geq lcp(a_{i+1},r_i)
fi,j,kf_{i,j,k}表示到第ii組,AiA_{i}和下一個左端點lcplcpjj,和當前右端點的lcplcpkk的答案。
轉移的時候滿足合法條件即可。
複雜度是O(2mm2)O(2^{m}m^{2})

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e8+7;
const int N=(1<<17)+1;
const int M=19; 
int l[N],r[N]; 
int f[N][M][M]; // f[i][j][k] -> cur=i,lcp(a_i,l_{i+1})=j,lcp(a_i,r_i)=k 
int n,m;
int lcp(int x,int y) {
	int ans=0;
	for(int i=m-1;i>=0;i--) {
		if((x&(1<<i))==(y&(1<<i))) ans++;
		else break;
	}
	return ans;
} 
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		scanf("%d%d",&m,&n);
		for(int i=1;i<=n;i++) 
			scanf("%d%d",&l[i],&r[i]);
		for(int i=0;i<=n;i++) 
			for(int j=0;j<=18;j++) 
				for(int k=0;k<=18;k++) 
					f[i][j][k]=0;
		f[0][0][m]=1;
		for(int i=1;i<=n;i++) {
			for(int a=l[i];a<=r[i];a++) {
				int lcpl=lcp(a,l[i]); // left
				int lcpr=lcp(a,r[i]); // right 
				int lstr=(i>=2)?lcp(a,r[i-1]):0;
				int nxtl=(i<n)?lcp(a,l[i+1]):0;
				int sum=0;
				for(int j=0;j<=lcpl;j++) {
					for(int k=lstr;k<=m;k++) {
						sum=(sum+f[i-1][j][k])%mod;
					}
				}
				f[i][nxtl][lcpr]+=(1ll*a*sum)%mod;
				f[i][nxtl][lcpr]%=mod;
			}
		}
		int ans=0;
		for(int i=0;i<=m;i++) {
			for(int j=0;j<=m;j++) {
				ans=(ans+f[n][i][j])%mod;
			}
		}
		printf("%d\n",ans); 
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章