題意比較複雜。
給出 表示我現在有這樣的一個區間,然後現在有表示我將會把這個區間分爲段區間,這段是覆蓋所有的區間並且嚴格不相交的。
然後在每個區間裏面都選一個數,然後要滿足對於任意一個區間,都有和這個區間中任何數的二進制表示的前綴最大值不小於和這個區間中任何數的二進制表示的前綴最大值。
如果存在滿足這樣的一個序列,它的貢獻是。求所有方案的貢獻之和,答案模。
其實冷靜思考一下,這個前綴最大值越大,說明這個數和當前的數越接近。
那麼對於任何一個區間裏面的數,我只要保證它和離它最遠的兩個端點都比旁邊的數和端點的值就可以了。
記錄表示和的前綴最大值。那麼限制條件爲:
表示到第組,和下一個左端點爲,和當前右端點的爲的答案。
轉移的時候滿足合法條件即可。
複雜度是
#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;
}