「CTSC2017」吉夫特(Gift) (Lucas定理,状压DP)
题面
简单的题目,既是礼物,也是毒药。
B 君设计了一道简单的题目,准备作为 gift 送给大家。
输入一个长度为
n
n
n 的数列
a
1
,
a
2
,
⋯
,
a
n
a_1, a_2, \cdots , a_n
a1,a2,⋯,an 问有多少个长度大于等于
2
2
2 的不上升的子序列满足:
∏
i
=
2
k
(
a
b
i
−
1
a
b
i
)
m
o
d
2
=
(
a
b
1
a
b
2
)
×
(
a
b
2
a
b
3
)
×
⋯
(
a
b
k
−
1
a
b
k
)
m
o
d
2
>
0
\prod_{i=2}^{k}\begin{pmatrix}a_{b_{i-1}}\\a_{b_i}\end{pmatrix}\mod 2=\begin{pmatrix}a_{b_{1}}\\a_{b_2}\end{pmatrix}×\begin{pmatrix}a_{b_{2}}\\a_{b_3}\end{pmatrix}×\cdots\begin{pmatrix}a_{b_{k-1}}\\a_{b_k}\end{pmatrix}\mod 2>0
i=2∏k(abi−1abi)mod2=(ab1ab2)×(ab2ab3)×⋯(abk−1abk)mod2>0
输出这个个数对
1000000007
1000000007
1000000007 取模的结果。
……
G 君听说这个题是作为 gift 送给大家,她有一句忠告。
“Vorsicht, Poison!”
“小心. . . . . .礼物! ”
输入格式
第一行一个整数 n n n。
接下来 n n n 行,每行一个整数,这 n n n 行中的第 i i i 行,表示 a i a_i ai 。
输出格式
一行一个整数表示答案。
数据范围
1
≤
n
≤
211985
1\leq n\leq 211985
1≤n≤211985,
1
≤
a
i
≤
233333
1\leq a_i\leq 233333
1≤ai≤233333 (不超过
2
18
2^{18}
218)。
所有的
a
i
a_i
ai 互不相同,也就是说不存在
i
,
j
i, j
i,j 同时满足
1
≤
i
<
j
≤
n
1\leq i < j\leq n
1≤i<j≤n 和
a
i
=
a
j
a_i = a_j
ai=aj 。
题解
相信大家和我一样,看到这道题就从 m o d 2 > 0 \!\!\!\mod 2>0 mod2>0 开始推起,转换成结果为奇数,然后中间每个组合数都得是奇数,然后再把组合数拆开看怎么达到奇数……
其实转化为每个 C ( a b i − 1 , a b i ) m o d 2 > 0 C(a_{b_{i-1}},a_{b_i})\mod 2>0 C(abi−1,abi)mod2>0 就足够了,本来我们看到模数这么小应该先想到Lucas定理的。
用Lucas定理化简一下
C
(
A
,
B
)
m
o
d
2
C(A,B)\mod 2
C(A,B)mod2 ,得到
C
(
A
&
2
0
,
B
&
2
0
)
∗
C
(
A
&
2
1
,
B
&
2
1
)
∗
C
(
A
&
2
2
,
B
&
2
2
)
∗
⋯
m
o
d
2
C(A\&2^0,B\&2^0)*C(A\&2^1,B\&2^1)* C(A\&2^2,B\&2^2)*\cdots\mod 2
C(A&20,B&20)∗C(A&21,B&21)∗C(A&22,B&22)∗⋯mod2因此,上式中每一个组合数都得是
1
1
1 ,结果才能是
1
1
1 。
根据上式,只会出现 C ( 1 , 1 ) , C ( 1 , 0 ) , C ( 0 , 0 ) , C ( 0 , 1 ) C(1,1),C(1,0),C(0,0),C(0,1) C(1,1),C(1,0),C(0,0),C(0,1) 这四种组合数,而只有 C ( 0 , 1 ) C(0,1) C(0,1) 不是 1 1 1 而是 0 0 0 ,所以不能出现 C ( 0 , 1 ) C(0,1) C(0,1) ,这就要求 B & A = B ( B ⊆ A ) B\;\&\;A=B\;\;(B\subseteq A) B&A=B(B⊆A)。
那么题意成功转化:找出长度大于等于 2 2 2 的下降子序列 a 1 , a 2 , ⋯ , a n a_1, a_2, \cdots , a_n a1,a2,⋯,an 满足二进制下后一个数是前一个数的子集。
这个其实用一个状压DP就很好做了,每一个数不同,因此可以每一种数字对应一个位置,转移时保证更大的数字位置更靠前(谨记),至于长度大于等于 2 2 2,倒没必要在边界上费心,只需要最后减去长度小于 2 2 2 的就行。
CODE
(认真整了点没用的优化)
#include<ctime>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define MAXN 211990
#define MAXM (1<<18|5)
#define DB double
#define ENDL putchar('\n')
#define INF ((LL)1e18)
#define lowbit(x) (-(x) & (x))
LL read() {
LL f=1,x=0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
const int MOD = 1000000007;
int n,m,s,i,j,o,k;
int a[MAXN];
int pt[MAXM],ct[MAXM];
int dp1[MAXM],dp2[MAXM],dp[MAXM];
int main() {
n = read();m = 1;int CT = 0;
for(int i = 1;i <= n;i ++) {
a[i] = read();
pt[a[i]] = i;
while(m-1 <= a[i]) m <<= 1,CT ++;
}
// printf("m:%d\n",m);
pt[0] = n+1;
for(int i = 1;i < m;i ++) ct[i] = ct[i-lowbit(i)] + 1;
dp1[m-1] = 1;
for(int i = m-2;i > 0;i --) {
if(ct[i]*2 >= CT && pt[i]) {
dp1[i] = 0;
int ni = (m-1) ^ i;
for(int j = ni;j > 0;j = (j-1)&ni) {
if(pt[i^j] < pt[i]) (dp1[i] += dp1[i^j]) %= MOD;
}
// printf("dp1[%d] = %d\n",i,dp1[i]);
}
}
dp2[0] = 1;
for(int i = 1;i < m-1;i ++) {
if(ct[i]*2 < CT && pt[i]) {
dp2[i] = 0;
for(int j = i;j > 0;j = (j-1)&i) {
if(pt[i^j] > pt[i]) (dp2[i] += dp2[i^j]) %= MOD;
}
// printf("dp2[%d] = %d\n",i,dp2[i]);
}
}
int ans = 0;
for(int i = 1;i < m;i ++) {
if(i == m-1 || (ct[i]*2 >= CT && pt[i])) {
for(int j = i;j > 0;j = (j-1)&i) {
if((ct[i^j]<<1) < CT && pt[i^j] > pt[i])
(dp[i] += dp1[i] *1ll* dp2[i^j] % MOD) %= MOD;
}
}
(ans += dp[i]) %= MOD;
}
(ans += MOD-(n+1)) %= MOD;
printf("%d\n",ans);
return 0;
}