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 ai1中的要多,
当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=0j1dp[i1][t]2tC(j,t)
意义是 a i − 1 ′ a_i-1' ai1中钦定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=0j1t!dp[i1][t]2t(jt)!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][jt]2mtC(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(jt)!dp[m][jt]
然后我们把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(jt)!dp[n][jt]
然后就可以愉快的倍增搞了,当然长度为奇数的情况下,需要当成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

CF623E Transforming Sequence

Educational Codeforces Round 9 E. Thief in a Shop NTT

Codeforces1096G Lucky Tickets(NTT优化dp)

Codeforces 827D Best Edge Weight 倍增 + 并查集 || 倍增 + 压倍增标记 (看题解)

Codeforces 1175E 倍增