zoukankan      html  css  js  c++  java
  • Codeforces 1326 F2 Wise Men (Hard Version)(容斥+FWT):

    https://codeforces.com/problemset/problem/1326/F2

    直接做已经不太能怎么优化了。

    考虑容斥,设(f[S]),S上的第(i)(=1)表示(s[p[i+1]][p[i+2]]=1)(=0)表示(s[p[i+1]][p[i+2]]=0/1),的方案数。

    如果能求出(f),那么真正的答案就是(f)的高维后缀差分。

    注意到(f[S])的值只和每一段连续1的长度有关。

    如果(S=1101100111),那么相当于有({1,3,3,4})这个可重集合,对于集合中每个元素(x),代表有(x)个元素相邻且相邻的有边。

    这个集合就相当于对(n)的整数拆分,当(n=18) 时有(385)种。

    那么可以搜索整数拆分,求出这个拆分的答案,再枚举重复元素的全排列去统计给f。

    接下来考虑知道了整数拆分如何求答案。

    预处理(g[S]) 表示(S)中的元素相邻且相邻有边的方案数。

    对于一个整数拆分(m_1+m_2+…+m_k=n),方案数就是:

    (sumprod_{|S_i|=m_i}~ g[S_i](S_1∪S_2∪…∪S_k=全集))

    (c[i][S]=g[S]*[|S|=i])

    最暴力的做法就是在枚举了所有的(m)后,利用(c[m_i]) FWT直接搞起来。

    事实上可以先对(c)做FWT,每枚举一个(m_i),就乘上(FWT(c[m_i]))

    最后统计答案时,因为只需要(11…1)那一位的值,并不需要做一遍完整的IFWT。

    FWT中求一位的值可以做到(2^n),推导可以得到下面的式子:

    (FWT_{or}^{-1}(A)[x^S]=sum_{iin S}A[i]*(-1)^{|i~xor~S|})

    时间复杂度:(O((385+n^2)*2^n))

    Code:

    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
    #define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
    #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    const int N = 19;
    
    
    int a2[N];
    
    int n;
    char str[N][N];
    int a[N][N];
    
    
    #define low(x) ((x) & -(x))
    int cnt[1 << 18];
    
    ll f[N][1 << 18], b[N][1 << 18];
    
    void fwt(ll *a, int n, int f) {
    	for(int i = 1; i < n; i *= 2) for(int j = 0; j < n; j += 2 * i) ff(k, 0, i)
    		a[i + j + k] += a[j + k] * f;
    }
    
    ll c[N][1 << 18];
    
    ll solve(ll *a) {
    	ll ans = 0;
    	ff(i, 0, a2[n]) ans += a[i] * ((n - cnt[i]) & 1 ? -1 : 1);
    	return ans;
    }
    
    ll ans[1 << 18];
    
    ll val;
    
    int d[N], m, p[N], us[N];
    
    void dfs(int x) {
    	if(x > m) {
    		int s = 0, t = 0;
    		fo(i, 1, m) {
    			fo(j, 1, p[i] - 1) s += a2[t ++];
    			t ++;
    		}
    		ans[s] += val;
    		return;
    	}
    	fo(i, 1, m) if(!us[i]) {
    		if(d[i] == d[i - 1] && !us[i - 1]) continue;
    		us[i] = 1; p[x] = d[i];
    		dfs(x + 1);
    		us[i] = 0;
    	}
    }
    
    void dg(int x, int y, int z) {
    	if(z == n) {
    		a0 ++;
    		val = solve(c[x]);
    		m = x;
    		dfs(1);
    		return;
    	}
    	if(z > n) return;
    	fo(i, y, n - z) {
    		ff(j, 0, a2[n]) c[x + 1][j] = c[x][j] * b[i][j];
    		d[x + 1] = i;
    		dg(x + 1, i, z + i);
    	}
    }
    
    ll e[1 << 18];
    
    int main() {
    	a2[0] = 1; fo(i, 1, 18) a2[i] = a2[i - 1] * 2;
    	scanf("%d", &n);
    	fo(i, 1, n) {
    		scanf("%s", str[i] + 1);
    		fo(j, 1, n) a[i][j] = str[i][j] - '0';
    	}
    	ff(i, 1, a2[n]) cnt[i] = cnt[i ^ low(i)] + 1;
    	fo(i, 1, n) f[i][a2[i - 1]] = 1;
    	ff(s, 1, a2[n]) {
    		fo(i, 1, n) if(f[i][s]) {
    			fo(j, 1, n) if(!(s >> (j - 1) & 1) && a[i][j]) {
    				f[j][s ^ a2[j - 1]] += f[i][s];
    			}
    		}
    	}
    	ff(s, 1, a2[n]) fo(i, 1, n) {
    		b[cnt[s]][s] += f[i][s];
    	}
    	fo(i, 1, n) fwt(b[i], a2[n], 1);
    	c[0][0] = 1; fwt(c[0], a2[n], 1);
    	dg(0, 1, 0);
    	fo(i, 0, n - 2) {
    		ff(s, 0, a2[n]) if(!(s >> i & 1))
    			ans[s] -= ans[s ^ a2[i]];
    	}
    	ff(i, 0, a2[n - 1]) pp("%lld ", ans[i]); hh;
    }
    
  • 相关阅读:
    springBoot、SpringCloud 常用注解
    HashMap
    数据库连接池原理
    三次握手《《=====》》四次握手
    服务器
    二维码
    Nginx
    日志记录
    数据库事务/索引/存储引擎/锁
    Java接口
  • 原文地址:https://www.cnblogs.com/coldchair/p/12534921.html
Copyright © 2011-2022 走看看