码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces 1326 F2 Wise Men (Hard Version)(容斥+FWT):

时间:2020-03-20 21:59:12      阅读:112      评论:0      收藏:0      [点我收藏+]

标签:ret   tps   一个   %s   接下来   space   rod   重复元素   长度   

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\),方案数就是:

\(\sum\prod_{|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_{i\in 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("\n")
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;
}

Codeforces 1326 F2 Wise Men (Hard Version)(容斥+FWT):

标签:ret   tps   一个   %s   接下来   space   rod   重复元素   长度   

原文地址:https://www.cnblogs.com/coldchair/p/12534921.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!