• bzoj3696 化合物


    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3696

    【题解】

    f[x][i]表示x节点子树,值为i的方案数。

    暴力统计理论复杂度$O(nH^2)$。

    按子树排序后统计复杂度好像还是错的。。但是能过

    明天研究FWT。。

    暴力跑了0.8s,FWT跑了3.1s。。。日

    FWT也要按深度合并啊。。FWT的介绍在最后

    # include <stdio.h>
    # include <string.h>
    # include <iostream>
    # include <algorithm>
    // # include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    const int M = 5e5 + 10, N = 1e5 + 10, H = 512 + 5;
    const int mod = 1e9+7;
    
    int n, f[N][H], g[N], par[N];
    ll ans[H]; 
    
    int t[N], tn;
    inline int cmp(int a, int b) {
        return g[a] < g[b];
    }
    
    int head[N], nxt[N], to[N], tot = 0;
    inline void add(int u, int v) {
        ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v;
    }
    
    ll ss[H], tt[H]; 
    
    inline void FWT(ll *a, int op, int L) {
        if(op) {
            for (int len=2; len<=L; len<<=1) {
                int m = len >> 1;
                for (ll *p = a; p != a+L; p += len) {
                    for (int k=0; k<m; ++k) {
                        ll x = p[k], y = p[k+m];
                        p[k] = (x+y)/2;
                        p[k+m] = (x-y)/2;
                    }
                }
            }
        } else {
            for (int len=2; len<=L; len<<=1) {
                int m = len >> 1;
                for (ll *p = a; p != a+L; p += len) {
                    for (int k=0; k<m; ++k) {
                        ll x = p[k], y = p[k+m];
                        p[k] = x+y;
                        p[k+m] = x-y;
                    }
                }
            }
        }
    }
    
    inline void FWT_combine(int *A, int an, int *B, int bn) {
        int L = 0, m = max(an, bn);
        while((1<<L) < m) ++L; m = (1<<L);
         for (int i=0; i<m; ++i) ss[i] = A[i], tt[i] = B[i];
        FWT(ss, 0, m), FWT(tt, 0, m);
        for (int i=0; i<m; ++i) ss[i] = ss[i] * tt[i];
        FWT(ss, 1, m);
        for (int i=0; i<m; ++i) ans[i] += ss[i];
    }
    
    inline void dfs(int x) {
        int ret = 1;
        f[x][0] = 1; 
        for (int i=head[x]; i; i=nxt[i]) dfs(to[i]);
        tn = 0; for (int i=head[x]; i; i=nxt[i]) t[++tn] = to[i]; 
        sort(t+1, t+tn+1, cmp);
        for (int i=1; i<=tn; ++i) { 
            int h = g[t[i]], y = t[i];
            FWT_combine(f[x], ret, f[t[i]], h);
            for (int j=0; j<h; ++j) f[x][j] += f[y][j];  
            ret = max(ret, h); 
        }
        for (int j=ret; j; --j) f[x][j] = f[x][j-1];
        f[x][0] = 0; 
        g[x] = ret + 1; 
    }
    
    
    int main() {
        cin >> n;
        for (int i=2; i<=n; ++i) scanf("%d", par+i), add(par[i], i); 
        dfs(1);    
        for (int i=511; ~i; --i)
            if(ans[i] != 0) {
                 for (int j=0; j<=i; ++j)
                     printf("%lld
    ", ans[j]);
                return 0;
            }
        return 0;
    }
    View Code

    下面是暴力:

    # include <stdio.h>
    # include <string.h>
    # include <iostream>
    # include <algorithm>
    // # include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    const int M = 5e5 + 10, N = 1e5 + 10, H = 512 + 5;
    const int mod = 1e9+7;
    
    int n, f[N][H], g[N], par[N];
    ll ans[H]; 
    
    int t[N], tn;
    inline int cmp(int a, int b) {
        return g[a] < g[b];
    }
    
    int head[N], nxt[N], to[N], tot = 0;
    inline void add(int u, int v) {
        ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v;
    }
    
    inline void dfs(int x) {
        int ret = 1;
        f[x][0] = 1; 
        for (int i=head[x]; i; i=nxt[i]) dfs(to[i]);
        tn = 0; for (int i=head[x]; i; i=nxt[i]) t[++tn] = to[i]; 
        sort(t+1, t+tn+1, cmp);
        for (int i=1; i<=tn; ++i) { 
            int h = g[t[i]], y = t[i];
            for (int j=0; j<ret; ++j) 
                for (int k=0; k<h; ++k)
                    ans[j ^ k] += 1ll * f[x][j] * f[y][k];
            for (int j=0; j<h; ++j) f[x][j] += f[y][j];  
            ret = max(ret, h); 
        }
        for (int j=ret; j; --j) f[x][j] = f[x][j-1];
        f[x][0] = 0; 
        g[x] = ret + 1; 
    }
    
    
    int main() {
        cin >> n;
        for (int i=2; i<=n; ++i) scanf("%d", par+i), add(par[i], i); 
        dfs(1);    
        for (int i=511; ~i; --i)
            if(ans[i] != 0) {
                 for (int j=0; j<=i; ++j)
                     printf("%lld
    ", ans[j]);
                return 0;
            }
        return 0;
    }
    View Code

    ===================================分割线==============================

    考虑集合的位运算卷积
    $C_i = sum_{j oplus k = i}A_jB_k$

    每次只需要考虑最高位,事先补齐集合至2的次幂。
    设符号$C = (A, B)$表示$C$由$A$和$B$顺次拼接而成。

    如果只考虑最高位的话,有:
    $ C = A oplus B = (sum_{j oplus k = 0} A_jB_k, sum_{j oplus k = 1} A_jB_k)$
    (这里的0和1是因为值考虑最高位的原因)

    这给了我们一个非常好的分治想法
    假设我们构造出变换$f$以及逆变换$f_T$,满足:
    $f(A)f(B) = f(C)$
    那么我们就用类似于FFT的方法实现即可。

    1. 如果是异或(xor)卷积的话:
    $A_0$和$A_1$是按照$A$的最高位分成的两部分
    $f(A) = (f(A_0) + f(A_1), f(A_0) - f(A_1))$
    $f_T(A) = (f_T(frac{A_0 + A_1}{2}), f_T(frac{A_0 - A_1}{2}))$
    正确性可以用归纳法证明。
    其实只要记住正变换的式子即可,逆变换可以从正变换推导出来。


    2. 如果是与(and)卷积的话
    $f(A) = (f(A_0) + f(A_1), f(A_1))$
    $f_T(A) = (f_T(A_0) - f_T(A_1), f_T(A_1))$

    3. 如果是或(or)卷积的话
    $f(A) = (f(A_0), f(A_1) + f(A_0))$
    $f_T(A) = (f_T(A_0), f_T(A_1) - f_T (A_0))$

  • 相关阅读:
    Android开发之基本控件和详解四种布局方式
    Android开发之Activity的创建跳转及传值
    设计模式(十三):从“FQ”中来认识代理模式(Proxy Pattern)
    设计模式(十二):通过ATM取款机来认识“状态模式”(State Pattern)
    设计模式(十一):从文Finder中认识"组合模式"(Composite Pattern)
    设计模式(十):从电影院中认识"迭代器模式"(Iterator Pattern)
    设计模式(八): 从“小弟”中来类比"外观模式"(Facade Pattern)
    设计模式(六):控制台中的“命令模式”(Command Pattern)
    设计模式(三):“花瓶+鲜花”中的装饰者模式(Decorator Pattern)
    设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)
  • 原文地址:https://www.cnblogs.com/galaxies/p/bzoj3696.html
Copyright © 2020-2023  润新知