• CF1408I


    CF1408I

    给定 (n,k,c),以及长度为 (n) 的序列 (a)(保证元素互不相同)。

    操作 (k) 次,每次随机选择一个 (a_i),然后将其 (-1)

    对于 (x=0,1~...~2^c-1) 输出最后序列的异或和为 (x) 的概率。

    答案对 (998244353) 取模。

    (k,cle 16,a_iin [k,2^c),nle 2^c-k)

    Solution

    观察到一个强有力的性质:

    • 考虑形如 ((xoplus (x-1),xoplus (x-2)...xoplus (x-k))) 这样的 (k) 元组,在 (x<2^c) 的时候,级别近似于 (mathcal O(ck))

    (c=16,k=16) 时搜出来的结果为 (192)

    我们考虑论证:

    观察到 (xoplus (x-1)) 的取值仅有 (2^t-1) 的形式。

    其中,其取 (2^t-1) 表示最低位的 (1) 位于 (t-1) 位。

    (t> log k),那么不难发现 (xoplus (x-2)...xoplus (x-k)) 均固定了。

    否则 (tle log k),那么后续答案只和其位于 (log k) 位往上走的第一位 (1) 所处的位置,和初始后续 (log k) 个位置的具体取值相关,这样可以产生的不同的 (k) 元组的数量为 (c imes 2^{log k}=ck) 这一级别 。

    所以本质不同的 (k) 元组仅有 (mathcal O(ck)) 种。

    考虑令 (t=a_1oplus a_2....oplus a_n)

    那么我们可以考虑答案在 (t) 上的改变量。

    从计数的角度考虑,我们使用 EGF 来统计操作序列的数量,不妨令 (d_{i,j}=a_ioplus (a_i-j)),不难发现我们要求的即:

    [prod_i^n igg(1+sum_j^kfrac{1}{j!}cdot x^{d_{i,j}}y^jigg)[y^k] imes k! ]

    其中,作用在 (x) 维度上的卷积为异或卷积,(y) 上则为加法卷积。

    于是我们得到了一个暴力做法:

    枚举这 (ck)(k) 元组,对其中每个元素固定 (y) 然后做 (FWT_{ extrm{xor}}) 变换,然后枚举 (x^{z}),对于某个 (z),答案的贡献可以描述为 (ck) 个关于 (y) 的多项式的若干次幂的乘积。取其 (y^k) 项即可。

    当然,由于固定 (y) 和枚举多项式之后,每个需要进行 FWT 的元素均为单项式 (x^{d_{i,j}}),所以我们可以暴力转 FWT

    这样直接枚举这 (ck) 个多项式,我们发现我们的任务是计算 (F(y)^m),于是直接在模 (y^{k+1}) 意义下进行暴力的 (ln)(exp) 变换(均为 (k^2)),设 (c,k) 平级,复杂度为 (mathcal O(c^4cdot 2^c)),但是 (ln)(exp) 的巨大常数使人无法通过此题。(反正我不行)

    我们能否做到更优呢?当然可以:

    观察到 (FWT_{ extrm{xor}}(j)) 为:

    [sum_i (-1)^{ extrm{popcount}(i& j)} ]

    对于某个 (z) 而言,我们原本想要计算 (ck) 个多项式的若干次幂卷积的结果。

    但是实际上,对于 (z) 而言,(k) 元组 ((a_1,a_2...a_k)) FWT 的结果等效于一个 (1/-1) 的序列 (((-1)^{a_1&z},....)) 的结果,这样的 1/-1 的序列可以状压下来。

    于是两个 (k) 元组如果得到的 (1/-1) 序列相同,那么 FWT 后得到的多项式将相同,于是他们可以压起来一起 (ln/exp) 从而减少运算次数。

    原本大概是 (mathcal O(c^2cdot 2^c)) 级别的运算量,但是加入此优化后需要计算的次数只有 (1.4cdot 10^6) 级别了,那么直接 (exp,ln) 和暴力卷积即可。

    复杂度大概当作 (mathcal O(c^3cdot 2^c)) 吧?

    优雅 の (exp)

    [B(z)=exp A(z) ]

    [[z^n]B'(z)=[z^n](A'(z)B(z)) ]

    [(n+1)[z^{n+1}]B(z)=sum_{k} (k+1)[z^{k+1}]A(z) imes B(z)[z^{n-k}] ]

    [[z^{n+1}]B(z)=frac{1}{n+1}sum_{k} (k+1)[z^{k+1}]A(z) imes B(z)[z^{n-k}] ]

    暴力即可。

    优雅 の (ln)

    [B(z)=ln A(z) ]

    [B(z)'=frac{A'(z)}{A(z)} ]

    [B(z)'A(z)=A'(z) ]

    [[z^{n+1}]A(z)(n+1)=sum_{i+j=n}[z^{i+1}]B(z)(i+1)[z^j]A(z) ]

    [A_{n+1}(n+1)=sum_{i=1}^{n+1}B_{i} imes i imes A_{n+1-i} ]

    [A_n imes n=sum_{i=1}^n B_i imes i imes A_{n-i} ]

    [B_n=A_n-frac{1}{n}sum_{i=1}^{n-1} B_i imes i imes A_{n-i} ]

    然后由于我太菜了,所以常数巨大,最后预处理了 (ln) 才过的这个题.../kk

    (Code:)

    
    #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #pragma GCC optimize( 
    	"inline,-fgcse,-fgcse-lm,-fipa-sra,-ftree-pre,-ftree-vrp,-fpeephole2,-ffast-math,-fsched-spec,unroll-loops,-falign-jumps,-falign-loops,-falign-labels,-fdevirtualize,-fcaller-saves,-fcrossjumping,-fthread-jumps,-funroll-loops,-freorder-blocks,-fschedule-insns,inline-functions,-ftree-tail-merge,-fschedule-insns2,-fstrict-aliasing,-fstrict-overflow,-falign-functions,-fcse-follow-jumps,-fsched-interblock,-fpartial-inlining,no-stack-protector,-freorder-functions,-findirect-inlining,-fhoist-adjacent-loads,-frerun-cse-after-loop,inline-small-functions,-finline-small-functions,-ftree-switch-conversion,-foptimize-sibling-calls,-fexpensive-optimizations,inline-functions-called-once,-fdelete-null-pointer-checks")
    #include<bits/stdc++.h>
    using namespace std ;
    #define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
    #define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
    #define re register
    #define int long long
    #define pb push_back
    #define vi vector<int>
    int gi() {
    	char cc = getchar() ; int cn = 0, flus = 1 ;
    	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
    	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
    	return cn * flus ;
    }
    const int P = 998244353 ; 
    const int N = (1 << 16) + 5 ; 
    const int M = 20 + 5 ;
    int fpow(int x, int k) {
    	int ans = 1, base = x ;
    	while(k) {
    		if(k & 1) ans = 1ll * ans * base % P ;
    		base = 1ll * base * base % P, k >>= 1 ;
    	} return ans ;
    }
    int n, K, m, lim, S, num, f[N], cnt[N], g[M], A[M], fac[M], inv[M], iv[M], st[N], top ; 
    int Ln[N][M] ; 
    map<vi, int> G ; 
    vector<int> sd[205] ; int sc[205] ; 
    int bit(int x) { return __builtin_popcount(x) ;}
    int Fm[M], Fp[M] ; 
    int mod(int x) { return (x - x / P * P) ; }
    void polydiv(int *a) { rep( i, 1, K ) a[i - 1] = i * a[i] % P ; a[K] = 0 ; }
    void polyln(int *a) {
    	rep( j, 0, K ) Fp[j] = 0 ; 
    	rep( j, 1, K ) {
    		int de = 0 ;
    		for(re int i = 1; i < j; ++ i) de = (de + mod(Fp[i] * a[j - i]) * i) ;
    		Fp[j] = (a[j] - de % P * iv[j] % P + P) % P ; 
    	} rep( j, 0, K ) a[j] = Fp[j] ; 
    }
    void polyexp(int *a) {
    	rep( j, 0, K ) Fp[j] = 0 ; polydiv(a), Fp[0] = 1 ; 
    	rep( j, 1, K ) {
    		for(int k = 0; k < j; ++ k) Fp[j] = (Fp[j] + mod(Fp[k] * a[j - k - 1])) ; 
    		Fp[j] = Fp[j] % P * iv[j] % P ; 
    	} rep( j, 0, K ) a[j] = Fp[j] ; 
    }
    signed main()
    {
    	n = gi(), K = gi(), m = gi(), lim = 1 << m ; 
    	for(int i = 1; i <= n; ++ i) {
    		int x = gi(); S ^= x ; vi d ; 
    		rep( j, 1, K ) d.pb(x ^ (x - j)) ; ++ G[d] ;
    	}
    	fac[0] = inv[0] = iv[0] = 1 ; 
    	rep( i, 1, K ) 
    		fac[i] = fac[i - 1] * i % P, inv[i] = fpow( fac[i], P - 2 ),
    		iv[i] = fpow( i, P - 2 ) ; 
    	for(auto x : G) ++ num, sd[num] = x.first, sc[num] = x.second ; 
    	int St = (1 << K) ; 
    	for(re int i = 0; i < St; ++ i) {
    		A[0] = 1 ; 
    		for(re int j = 1; j <= K; ++ j) 
    		A[j] = ((i >> (j - 1)) & 1) ? P - inv[j] : inv[j] ; 
    		polyln(A) ; 
    		rep( j, 0, K ) Ln[i][j] = A[j] ; 
    	}
    	for(int t = 0; t < lim; ++ t) {
    		for(int i = 1; i <= num; ++ i ) {
    			int c = sc[i], z = 0 ; 
    			for(re int j = 0; j < K; ++ j) 
    				if( bit(sd[i][j] & t) & 1 ) z |= (1 << j) ; //0 mean 1, 1 mean -1
    			if( !cnt[z] ) st[++ top] = z ; cnt[z] += c ;
    		}
    		rep( j, 0, K ) g[j] = 0 ; g[0] = 1 ; 
    		for(int x = 1; x <= top; ++ x) {
    			int z = st[x] ; 
    			rep( j, 0, K ) A[j] = Ln[z][j] * cnt[z] % P ; 
    			polyexp(A) ; 
    			for(re int j = K; j >= 0; -- j) {
    				int tmp = 0 ; 
    				rep( k, 0, j ) tmp = (tmp + mod(g[k] * A[j - k])) ; 
    				g[j] = tmp % P ; 
    			}
    		}
    		f[t] = g[K] * fac[K] % P ; rep( i, 1, top ) cnt[st[i]] = 0 ; top = 0 ; 
    	}
    	for(int k = 1; k < lim; k <<= 1) {
    		for(int i = 0; i < lim; i += (k << 1)) 
    		for(int j = i; j < i + k; ++ j) {
    			int nx = f[j], ny = f[j + k] ;
    			f[j] = (nx + ny) % P, f[j + k] = (nx - ny + P) % P ;  
    		}
    	} 
    	int di = fpow(lim, P - 2) * fpow(n, P - 1 - K) % P ; 
    	for(re int i = 0; i < lim; ++ i) printf("%lld ", f[i ^ S] * di % P ) ; 
    	return 0 ;
    }
    
  • 相关阅读:
    用Lua编写ACM算法竞赛开灯问题
    糟糕的中文版龙书
    font and face, 浅探Emacs字体选择机制及部分记录
    栈与卡特兰数
    关于2018年东南大学Robomaster算法组工作的总结
    C++中的默认参数规则
    MySQL第三章——嵌套查询
    MySQL第三章——空值的处理
    MySQL第三章——数据更新
    MySQL第三章——连接查询
  • 原文地址:https://www.cnblogs.com/Soulist/p/13758388.html
Copyright © 2020-2023  润新知