题意比较复杂。
给出
m
≤
17
m\leq17
m≤17 表示我现在有
[
0
,
2
m
−
1
]
[0,2^{m}-1]
[0,2m−1]这样的一个区间,然后现在有
0
≤
N
≤
2
m
0\leq N \leq 2^{m}
0≤N≤2m表示我将会把这个区间分为
N
N
N段区间,这
N
N
N段是覆盖所有的区间并且严格不相交的。
然后在每个区间里面都选一个数
A
i
∈
[
L
i
,
R
i
]
A_{i}\in[L_{i},R_{i}]
Ai∈[Li,Ri],然后要满足对于任意一个区间,都有
A
i
A_{i}
Ai和这个区间中任何数的二进制表示的前缀最大值不小于
A
j
A_{j}
Aj和这个区间中任何数的二进制表示的前缀最大值。
如果存在满足这样的一个序列,它的贡献是
∏
i
=
1
N
A
i
\prod_{i=1}^{N}A_{i}
∏i=1NAi。求所有方案的贡献之和,答案模
1
e
8
+
7
1e8+7
1e8+7。
其实冷静思考一下,这个前缀最大值越大,说明这个数和当前的数越接近。
那么对于任何一个区间里面的数,我只要保证它和离它最远的两个端点都比旁边的数和端点的值就可以了。
记录
l
c
p
(
x
,
y
)
lcp(x,y)
lcp(x,y)表示
x
x
x和
y
y
y的前缀最大值。那么限制条件为:
l
c
p
(
a
i
,
l
i
)
≥
l
c
p
(
a
i
−
1
,
l
i
)
lcp(a_i,l_i)\geq lcp(a_{i-1},l_i)
lcp(ai,li)≥lcp(ai−1,li)
l
c
p
(
a
i
,
r
i
)
≥
l
c
p
(
a
i
+
1
,
r
i
)
lcp(a_i,r_i)\geq lcp(a_{i+1},r_i)
lcp(ai,ri)≥lcp(ai+1,ri)
f
i
,
j
,
k
f_{i,j,k}
fi,j,k表示到第
i
i
i组,
A
i
A_{i}
Ai和下一个左端点
l
c
p
lcp
lcp为
j
j
j,和当前右端点的
l
c
p
lcp
lcp为
k
k
k的答案。
转移的时候满足合法条件即可。
复杂度是
O
(
2
m
m
2
)
O(2^{m}m^{2})
O(2mm2)
#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;
}