• UNR2 黎明前的巧克力


    C 黎明前的巧克力 [* hard]

    给定数列 (a),长度为 (n),保证 (n,a_ile 10^6),求有多少种方案选出两个集合 (A,B) 使得两个集合的异或和相同,不能均为空集,答案对 (998244353) 取模。

    ( m Sol:)

    先考虑 (A+B) 的异或和,必然是 (0)

    然后考虑 (A+B) 的大小为 (k),那么方案数为 (2^k)

    问题等价于求有 (n) 个元素,每个元素可以选/不选,对于 (i=1,2sim n) 求有多少种方案选出 (i) 个数使得权值异或和为 (0)

    事实上可以考虑将每个元素视为 ((1+2x^{a_i})) 这样的一个 Xor 多项式,这样的话 ({oplus}_{i=1}^n (1+2x^{a_i}))(x^0) 就是答案了。

    然后我们计算 Xor FWT,问题计算的本质上也是卷积结果,不妨考虑 (1+2x^{a_i}) 这个向量 ((F(x))) ?的 Xor_FWT 结果是啥。

    我们记得 ( m Xor ~FWT) 的矩阵为 (c(0,0),c(1,0),c(0,1)=1,c(1,1)=-1),所以 FWT 理论告诉我们:

    [ extrm{FWT}(F(x))_i=sum_{j=0} f_j imes (-1)^{ extrm{popcount}(i& j)} ]

    现在需要计算

    [prod extrm{FWT}(1+2x^{a_i})_j=prod igg(sum_k f_k imes (-1)^{ extrm{popcount}(i&k)}igg) ]

    另一边,我们注意到似乎 ( extrm{FWT}(F(x))_j) 的值只有 (-1)(3) 两种取值。

    同时,我们只需要知道 (prod extrm{FWT}(F(x))_j) 即可

    注意到每个位置不是 (-1) 就是 (3),那么假设有 (x)(-1) 那么就有 ((n-x))(3)

    另一边,我们注意到 (sum extrm{FWT}(F(x))= extrm{FWT}(sum F(x))),假设对于 (i) 而言,结果为 (f_i),那么有 (-x+(n-x)3=f_i o 3n-4x=f_i) 于是就可以把 (x) 解出来。

    然后就可以快乐的 IFWT 了。

    代码极其好写

    (Code:)

    #include<bits/stdc++.h>
    using namespace std ;
    #define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
    #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
    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 N = 2e6 + 5 ;
    const int P = 998244353 ; 
    const int I = 499122177 ;
    int n, m, limit, A[N], c[N], fac[N] ; 
    void FWT_xor( int *a, int type ) {
    	for( re int k = 1; k < limit; k <<= 1 ) 
    	for( re int i = 0; i < limit; i += ( k << 1 ) )
    	for( re int j = i; j < i + k; ++ j ) {
    		int nx = a[j], ny = a[j + k] ;
    		a[j] = ( nx + ny ) % P, a[j + k] = ( nx - ny + P ) % P ; 
    		if( !type ) a[j] = a[j] * I % P, a[j + k] = a[j + k] * I % P ; 
    	}
    }
    signed main()
    {
    	n = gi(), limit = 1, m = 0 ; 
    	rep( i, 1, n ) A[i] = gi(), m = max( m, A[i] ), c[A[i]] += 2 ; 
    	while( limit <= m ) limit <<= 1 ; fac[0] = 1 ; 
    	for( re int i = 1; i <= 2 * n; ++ i ) fac[i] = fac[i - 1] * 3 % P ; 
    	c[0] += n, FWT_xor( c, 1 ) ;
    	for( re int i = 0; i < limit; ++ i ) {
    		int x = ( 3 * n - c[i] + P ) % P ;
    		x = x * I % P * I % P, A[i] = fac[n - x] ;
    		if( x & 1 ) A[i] = ( P - A[i] ) ;
    	}
    	FWT_xor( A, 0 ) ;
    	printf("%lld
    ", ( P + A[0] - 1 ) % P ) ;
    	return 0 ;
    }
    
  • 相关阅读:
    【域控】获取域控用户
    【MongoDB】开启认证权限
    【MongoDB】 安装为windows services
    【 Quartz】使用 JobListener (任务监听器可实现) 我想在一个任务执行后在执行第二个任务怎么办呢
    【多路复用】I/O多路复用
    静态类和静态类成员
    C#
    response.redirect和server.Transfer的差别详解
    DataReader
    受管制的代码和强类型系统
  • 原文地址:https://www.cnblogs.com/Soulist/p/13653444.html
Copyright © 2020-2023  润新知