CodeForces - 623E(倍增+ntt)
Posted 吃花椒的妙酱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CodeForces - 623E(倍增+ntt)相关的知识,希望对你有一定的参考价值。
题目大意: 一个序列的变换定义为对于每个ai变成or_sum[i],or_sum[i]表示前i个数的或值和(ai的或和),问有多少个长度为n的序列满足变换后是个严格递增序列(n<=1e18,k<=3e4)
思路:
设
a
i
′
ai'
ai′表示变换后位置i上的数
想一下,不难发现变换后的ai’对应的二进制中1的数量比
a
i
−
1
a_i-1
ai−1中的要多,
当n>k时显然ans=0。
将ai看成01串后,1的位置并不重要了,如果能求出f(i),f(i)表示
a
n
′
a_n'
an′中有i个1的合法序列数,那么
a
n
s
=
∑
i
=
n
k
f
(
i
)
∗
C
(
k
,
i
)
ans=\\sum_i=n^kf(i)*C(k,i)
ans=∑i=nkf(i)∗C(k,i)
考虑dp,设dp[i][j]表示前i个数组成的合法序列数且
a
i
’
a_i’
ai’中有j个1,我们要求的就是f(i) = dp[n][i]。
考虑从i-1推到i,有转移方程
d
p
[
i
]
[
j
]
=
∑
t
=
0
j
−
1
d
p
[
i
−
1
]
[
t
]
∗
2
t
∗
C
(
j
,
t
)
dp[i][j]=\\sum_t=0^j-1dp[i-1][t]*2^t*C(j,t)
dp[i][j]=∑t=0j−1dp[i−1][t]∗2t∗C(j,t)
意义是
a
i
−
1
′
a_i-1'
ai−1′中钦定t个位置是1然后
a
i
′
a_i'
ai′中对应的这t个位置可选1可不选。
直接暴力转移是
O
(
n
k
2
)
O(nk^2)
O(nk2)的。
展开一下组合数,移项得:
d
p
[
i
]
[
j
]
j
!
=
∑
t
=
0
j
−
1
d
p
[
i
−
1
]
[
t
]
∗
2
t
t
!
∗
1
(
j
−
t
)
!
\\fracdp[i][j]j!=\\sum_t=0^j-1\\fracdp[i-1][t]*2^tt!*\\frac1(j-t)!
j!dp[i][j]=∑t=0j−1t!dp[i−1][t]∗2t∗(j−t)!1
观察到后面是个卷积形式,用fft优化后是O(nklogk),再加上n次fft的大肠数,差不多2e10以上的量级了,还是过不了。
上面的式子还可以继续拓展(自己想不到qaq,更多是从数学意义上推的)
d
p
[
n
+
m
]
[
j
]
=
∑
t
=
0
j
d
p
[
n
]
[
t
]
∗
d
p
[
m
]
[
j
−
t
]
∗
2
m
t
∗
C
(
j
,
t
)
dp[n+m][j]=\\sum_t=0^jdp[n][t]*dp[m][j-t]*2^mt*C(j,t)
dp[n+m][j]=∑t=0jdp[n][t]∗dp[m][j−t]∗2mt∗C(j,t)
意义是长度为n+m的序列,前部分
a
n
a_n
an有t个1,后半部分有m个数,每个数这个t个1可选可不选,其他意义同上。
展开组合数,移项得:
d
p
[
n
+
m
]
[
j
]
j
!
=
∑
t
=
0
j
d
p
[
n
]
[
t
]
∗
2
m
t
t
!
∗
d
p
[
m
]
[
j
−
t
]
(
j
−
t
)
!
\\fracdp[n+m][j]j!=\\sum_t=0^j\\fracdp[n][t]*2^mtt!*\\fracdp[m][j-t](j-t)!
j!dp[n+m][j]=∑t=0jt!dp[n][t]∗2mt∗(j−t)!dp[m][j−t]
然后我们把m换成n得:
d
p
[
2
n
]
[
j
]
j
!
=
∑
t
=
0
j
d
p
[
n
]
[
t
]
∗
2
n
t
t
!
∗
d
p
[
n
]
[
j
−
t
]
(
j
−
t
)
!
\\fracdp[2n][j]j!=\\sum_t=0^j\\fracdp[n][t]*2^ntt!*\\fracdp[n][j-t](j-t)!
j!dp[2n][j]=∑t=0jt!dp[n][t]∗2nt∗(j−t)!dp[n][j−t]
然后就可以愉快的倍增搞了,当然长度为奇数的情况下,需要当成m = n+1,单独卷一次。
这样只要卷logn次,复杂度O(klogklogn),由于模数是1e9+7,还要做任意模数。常数十分大,博主用的3模数ntt,常数直接起飞,6100ms过的233。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false),cin.tie(nullptr)
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define pb(v) push_back(v)
#define all(v) v.begin(),v.end()
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x7f7f7f7f7f7f7f7f
#define endl "\\n"
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define AC return 0
#define ldb long double
const int N=1e6+5;
const double eps=1e-8;
const int maxn = 6e6+10;
int Pow(int x,int d,int p)
int tans = 1;
if(d == 0)return 1%p;
int a = Pow(x,d/2,p);
tans = 1ll*a*a%p;
if(d%2)tans = 1ll*tans*x%p;
return tans%p;
typedef vector<int> Poly;//多项式定义
int F1[maxn],F2[maxn];
int rev[maxn];
void NTT(int * A,int lim,int opt,int p)
int i, j, k, m, gn, g, tmp;
for(int i = 0; i < lim; ++i)rev[i] = (i & 1)*(lim >> 1) + (rev[i >> 1] >> 1);
for(i = 0; i < lim; ++i)if (rev[i] < i) swap(A[以上是关于CodeForces - 623E(倍增+ntt)的主要内容,如果未能解决你的问题,请参考以下文章
codeforces 623E Transforming Sequence
Educational Codeforces Round 9 E. Thief in a Shop NTT
Codeforces1096G Lucky Tickets(NTT优化dp)
Codeforces 827D Best Edge Weight 倍增 + 并查集 || 倍增 + 压倍增标记 (看题解)