zoukankan      html  css  js  c++  java
  • 树 [虚树, 动态规划]



    color{red}{正解部分}

    建出以 rr 为根的 虚树, 将 关键点 按照 虚树 中的深度排序, 这样可以保证一个点的祖先全部在其前面,

    F[i,j]F[i, j] 表示前 ii 个点, 分为 jj 组 的 方案数, g[i]g[i] 表示 iirr 的路径上 关键点 的个数,

    因为 ii 的祖先们必定不是在同一个组内,

    所以可以这样转移: F[i,j]=(ig[ai])×F[i1,j]+F[i1,j1]F[i, j] = (i - g[a_i]) imes F[i-1, j] + F[i-1,j-1], 最后 ans=i=1MF[k,i]ans =sumlimits_{i=1}^M F[k,i] .


    color{red}{实现部分}

    #include<bits/stdc++.h>
    #define reg register
    
    int read(){
            char c;
            int s = 0, flag = 1;
            while((c=getchar()) && !isdigit(c))
                    if(c == '-'){ flag = -1, c = getchar(); break ; }
            while(isdigit(c)) s = s*10 + c-'0', c = getchar();
            return s * flag;
    }
    
    const int mod = 1e9 + 7;
    const int maxn = 1e5 + 5;
    
    int N;
    int K;
    int M;
    int R;
    int K_;
    int Q_;
    int hs_cnt;
    int dfs_tim;
    int num0[2];
    int a[maxn];
    int b[maxn];
    int g[maxn];
    int Hs[maxn];
    int dfn[maxn];
    int stk[maxn];
    int dep0[maxn];
    int F[maxn][305];
    int Fk[maxn][20];
    int head[2][maxn];
    
    bool vis[maxn];
    bool used[maxn];
    
    struct Edge{ int nxt, to; } edge[2][maxn << 1];
    
    void Add(int from, int to, int id){ edge[id][++ num0[id]] = (Edge){ head[id][from], to }; head[id][from] = num0[id]; }
    
    void DFS_0(int k, int fa){ //
            dfn[k] = ++ dfs_tim; dep0[k] = dep0[fa] + 1;
            for(reg int i = 1; i <= 19; i ++) Fk[k][i] = Fk[Fk[k][i-1]][i-1];
            for(reg int i = head[0][k]; i; i = edge[0][i].nxt){ int to = edge[0][i].to; if(to == fa) continue ; Fk[to][0] = k; DFS_0(to, k); }
    }
    
    int Lca(int a, int b){ //
            if(dep0[a] < dep0[b]) std::swap(a, b);
            for(reg int i = 19; i >= 0; i --) if(dep0[Fk[a][i]] >= dep0[b]) a = Fk[a][i]; if(a == b) return a;
            for(reg int i = 19; i >= 0; i --) if(Fk[a][i] != Fk[b][i]) a = Fk[a][i], b = Fk[b][i]; return Fk[a][0];
    }
    
    bool cmp_1(int a, int b){ return dfn[a] < dfn[b]; }
    
    bool cmp_2(int a, int b){ return g[a] < g[b]; }
    
    int Dis(int u, int v){ return dep0[u] + dep0[v] - 2*dep0[Lca(u, v)]; }
    
    void Build(){
            std::sort(a+1, a+K+1, cmp_1);
            int top = 0; stk[++ top] = 1;
            for(reg int i = 1 + (a[1]==1); i <= K; i ++){
                    int u = stk[top], v = a[i];
                    int p = Lca(u, v);
                    if(!vis[p]) Hs[++ hs_cnt] = p, vis[p] = 1;
                    if(p == u) stk[++ top] = v;
                    else{
                            while(dep0[stk[top-1]] >= dep0[p] && top >= 2)
                                    Add(stk[top-1], stk[top], 1), Add(stk[top], stk[top-1], 1), top --;
                            if(stk[top] != p) Add(p, stk[top], 1), Add(stk[top], p, 1), stk[top] = p;
                            stk[++ top] = v;
                    }
            }
            for(reg int i = 1; i <= top; i ++)
                    if(i != top) Add(stk[i], stk[i+1], 1), Add(stk[i+1], stk[i], 1);
    }
    
    void DFS(int k, int fa){
            g[k] = g[fa] + used[fa];
            for(reg int i = head[1][k]; i; i = edge[1][i].nxt){ int to = edge[1][i].to; if(to == fa) continue ; DFS(to, k); }
    }
    
    void Dp(){
            std::sort(b+1, b+K_+1, cmp_2);
            F[0][0] = 1;
            for(reg int i = 1; i <= K_; i ++)
                    for(reg int j = 1; j <= M; j ++) F[i][j] = 0;
            for(reg int i = 1; i <= K_; i ++)
                    for(reg int j = 1; j <= M; j ++){
                            F[i][j] = F[i-1][j-1]; 
                            if(j > g[b[i]]) F[i][j] = (F[i][j] + 1ll*F[i-1][j]*(j-g[b[i]])%mod) % mod;
                    }
            int Ans = 0;
            for(reg int i = 1; i <= M; i ++) Ans = (Ans + F[K_][i]) % mod;
            printf("%d
    ", Ans);
    }
    
    void Work(){
            K = read(), M = read(), R = read();
            int flag = 0; hs_cnt = 0;
            for(reg int i = 1; i <= K; i ++){
                    b[i] = a[i] = read(); 
                    used[a[i]] = vis[a[i]] = 1;
                    Hs[++ hs_cnt] = a[i]; 
                    if(a[i] == R) flag = 1;
            }
            K_ = K;
            if(!flag) a[++ K] = R, Hs[++ hs_cnt] = R, vis[R] = 1;
            Build(); DFS(R, 0); Dp();
            num0[1] = 0; for(reg int i = 1; i <= hs_cnt; i ++) head[1][Hs[i]] = used[Hs[i]] = vis[Hs[i]] = 0;
    }
    
    int main(){
            freopen("tree.in", "r", stdin);
            freopen("tree.out", "w", stdout);
            N = read(); Q_ = read();
            for(reg int i = 1; i < N; i ++){ int u = read(), v = read(); Add(u, v, 0), Add(v, u, 0); }
            DFS_0(1, 0); while(Q_ --) Work(); 
            return 0;
    }
    
  • 相关阅读:
    oracle 聚合函数 LISTAGG ,将多行结果合并成一行
    oracle 数据库对于多列求最大值
    Java 简单的rpc 一
    centos7 安装php7
    win10下VM 中centos 安装共享文件
    CentOS7 cannot find a valid baseurl for repo base
    分布式事务
    利用虚拟映射文件加密大文件
    动态代理
    c++ 11 lambda表达式
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822425.html
Copyright © 2011-2022 走看看