zoukankan      html  css  js  c++  java
  • [九省联考2018]秘密袭击coat

    题目描述

    Luogu
    题目大意:给一棵(n)个点的树,求所有联通块中第(K)大的权值(W_k)之和。
    数据范围:(Kleq nleq 1666) , (W_{max}leq 1666),答案对(64123)取模,时限(7sec)

    题解

    (Ans = sum_{S} Kth of S = sum_{v = 1}^W vsum_{S} [Kth of S = v])
    (Ans = sum_{v = 1}^W sum_{S} [Kth of S ge v])
    我们令(cnt(S,v))表示(S)中权值大于等于(v)的节点个数。
    (Ans = sum_{v=1}^W sum_{S} [cnt(S,v)ge K])
    然后就可以设计一个(dp)了,设(f_{u,v,j})表示(u)为根的联通块中,(Wge v)联通块数。
    转移显然:(f_{u,v,j} = prod_{son_i} f_{son_i,v,k_{son_i}}),其中(sum_{son_i} k_{son_i} = j - [W_uge v])
    根据上述可得:(Ans = sum_{u} sum_{v=1}^W sum_{j=K}^n f_{u,v,j})
    卡一下(j)那一维,用树上(lca)那套分析一下,复杂度就是严格(O(n^2W))的。
    然后竟然就能成功AC原题数据了qaq......
    不管了。
    可以注意到(j)那一维是一个背包,所以自然就能想到生成函数。
    (F_{u,v} = sum_{j=0}^n f_{u,v,j} x^j),设(G_{u,v} = sum_{sin Tree_u} F_{s,v} = sum_{j=0}^n g_{u,v,j}x^j),那么(F)的转移就是一个卷积了。
    即初始化后,(F_{u,v} = prod_{son_i} F_{son_i,v})
    (Ans = sum_{u} sum_{v=1}^W sum_{j=K}^n f_{u,v,j} = sum_{v=1}^W sum_{j=K}^n g_{1,v,j} = sum_{j=K}^n sum_{v=1}^W g_{1,v,j})
    我们的目标即求(sum_{v=1}^W G_{1,v})的每一项系数。
    每次转移都卷积显然是傻子。
    熟悉(FFT)原理的童鞋都知道先用点值表示,最后再拉格朗日插值回去即可得到每一项的系数。
    外部枚举(x = 1,2...n+1),下面我们来考虑如何计算(F)(G)的点值表示。
    注意到由于转了点值表示,所以多项式乘法是对位乘法,就可以用线段树合并维护了。
    线段树每个叶子节点(v)维护点值(F_{u,v},G_{u,v}),我们在每个点(u)要干这些事:

    • 初始化:把区间([1,W_u])(F)加上(x),把区间((W_u,W_{max}])(F)加上(1)
    • 合并:把(F_{son_i})对位相乘,(G_{son_i})对位相加。
    • 结束:把(G_{u})加上(F_u),把(F_u)(1),便于下次转移。

    维护一个标记((a,b,c,d)),表示((F,G) o F(aF + b , cF + d + G))
    那么初始化对应标记((1,x,0,0))((1,1,0,0))。结束对应标记((1,1,1,0))
    合并的时候,线段树合并,设合并(x)(y)
    若其中一个点(以(y)为例)没有儿子了,也就是说下面的节点的((F,G))都是一样的了。
    此时(F_y = b)(G_y = d),对应((F_x,G_x) o (F_xF_y,G_x+G_y)),修改(x)的标记,然后(return)即可。
    最后把根节点的线段树遍历一遍,标记都放下去后叶子节点(v)的标记中的(d)(G_{1,v})
    最后套一下拉格朗日公式:(H(x) = sum_{i=1}^{n+1} H(i) prod_{j eq i} frac{x-j}{i-j})
    你说每次算(prod(x - j))(O(n^2))的?
    多项式除法了解一下蟹蟹qwq......先别管(j eq i)就行了。
    复杂度(O(n^2logW))被暴力吊着打,代码其实挺短的。

    实现代码

    #include<bits/stdc++.h>
    #define IL inline
    #define _ 2005
    #define ll long long
    #define ld long double
    using namespace std ; 
    
    IL ll gi(){
        ll data = 0 , m = 1; char ch = 0 ; 
        while((ch != '-') && (ch < '0' || ch > '9')) ch = getchar() ; 
        if(ch == '-'){m = 0 ; ch = getchar() ; }
        while(ch >= '0' && ch <= '9'){data = (data<<1) + (data<<3) + (ch^48) ; ch = getchar() ; }
        return (m) ? data : -data ; 
    } 
    
    #define mod 64123 
    
    int n , K , W , oo , stk[_ * _] , ans[_] , f[_] , fz[_] , H[_] , Ans , inv[mod] , rt[_] , val[_] ;  
    
    struct _Edge{
        int to , next ; 
    }Edge[_ << 1] ; int head[_] , CNT ; 
    IL void AddEdge(int u , int v) {
        Edge[++ CNT] = (_Edge){v , head[u]} ; head[u] = CNT ; return ; 
    }
    
    struct Target {
        int a , b , c , d ; 
        IL Target() {a = 1 ; b = 0 ; c = 0 ; d = 0 ; } 
        IL Target(int s1,int s2,int s3,int s4) {a = s1 ; b = s2 ; c = s3 ; d = s4 ; }
    } ;
    IL Target operator + (Target A , Target B) {
        Target C ; 
        C.a = 1ll * A.a * B.a % mod ; C.b = (1ll * B.a * A.b % mod + B.b) % mod ; 
        C.c = (1ll * A.a * B.c % mod + A.c) % mod ; 
        C.d = (1ll * A.b * B.c % mod + A.d + B.d) % mod ; 
        return C ; 
    }
    struct Node {
        int ls , rs ; Target tag ; 
        IL Node(){ls = rs = 0 ; tag = Target() ; return ; }
    }t[_ * _] ; 
    
    IL int NewNode() {
        if(stk[0]) {t[stk[stk[0]]] = Node() ; return stk[stk[0] --] ; }
        else {t[++oo] = Node() ; return oo ; }
    }
    void PushDown(int o) {
        if(!t[o].ls) t[o].ls = NewNode() ; if(!t[o].rs) t[o].rs = NewNode() ; 
        t[t[o].ls].tag = t[t[o].ls].tag + t[o].tag ; 
        t[t[o].rs].tag = t[t[o].rs].tag + t[o].tag ; 
        t[o].tag = Target() ; 
        return ;  
    }
    void Insert(int &o , int l , int r , int ql , int qr , Target E) {
        if(!o) o = NewNode() ; if(ql <= l && r <= qr) {t[o].tag = t[o].tag + E ; return ; }
        int mid = (l + r) >> 1 ; 
        PushDown(o) ; 
        if(ql <= mid) Insert(t[o].ls , l , mid , ql , qr , E) ; 
        if(qr  > mid) Insert(t[o].rs , mid + 1 , r , ql , qr , E) ; 
        return ; 
    }
    int Merge(int o , int os) {
        if(!o || !os) return o + os ; 
        if(!t[o].ls && !t[o].rs) swap(o , os) ; 
        if(!t[os].ls && !t[os].rs) {
            t[o].tag.a = 1ll * t[o].tag.a * t[os].tag.b % mod ; 
            t[o].tag.b = 1ll * t[o].tag.b * t[os].tag.b % mod ; 
            t[o].tag.d = (t[o].tag.d + t[os].tag.d) % mod ; 
            stk[++stk[0]] = os ; 
            return o ; 
        }
        PushDown(o) ; PushDown(os) ;
        t[o].ls = Merge(t[o].ls , t[os].ls) ; 
        t[o].rs = Merge(t[o].rs , t[os].rs) ; 
        stk[++stk[0]] = os ; 
        return o ; 
    }
    
    void Dfs(int u , int From , int x) {
        Insert(rt[u] , 1 , W , 1 , val[u] , Target(1 , x , 0 , 0)) ; 
        if(val[u] + 1 <= W) Insert(rt[u] , 1 , W , val[u] + 1 , W , Target(1 , 1 , 0 , 0)) ; 
        for(int e = head[u] ; e ; e = Edge[e].next) {
            int v = Edge[e].to ; if(v == From) continue ; 
            Dfs(v , u , x) ;
            rt[u] = Merge(rt[u] , rt[v]) ;  
        }
        t[rt[u]].tag = t[rt[u]].tag + Target(1 , 1 , 1 , 0) ; return ; 
    }
    
    void GetAns(int o , int l , int r , int x) {
        if(l == r) {H[x] = (H[x] + t[o].tag.d) % mod ; return ; }
        PushDown(o) ;
        int mid = (l + r) >> 1 ; 
        GetAns(t[o].ls , l , mid , x) ; GetAns(t[o].rs , mid + 1 , r , x) ; 
        return ;
    }
    IL void Solve(int x) {
        oo = 0 ; stk[0] = 0 ; for(int i = 1; i <= n; i ++) rt[i] = 0 ; 
        Dfs(1 , 0 , x) ;
        H[x] = 0 ; GetAns(rt[1] , 1 , W , x) ; 
    }
    
    IL void Lagrange() {
        inv[0] = 1 ; inv[1] = 1 ; for(int i = 2; i < mod; i ++) inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod ; 
        fz[0] = 1 ; 
        for(int i = 1; i <= n + 1; i ++) 
            for(int j = n + 1; j >= 0; j --) 
                if(j) fz[j] = (fz[j - 1] + 1ll * fz[j] * (mod - i) % mod) % mod ;  else fz[j] = 1ll * fz[j] * (mod - i) % mod ;  
        for(int i = 1; i <= n + 1; i ++) {
            int coef = 1 ; 
            for(int j = 1; j <= n + 1; j ++) if(i != j) coef = 1ll * coef * (i + mod - j) % mod ; 
            for(int j = 0; j <= n + 1; j ++) f[j] = fz[j] ;
            for(int j = 0; j <= n + 1; j ++) {
                if(j) f[j] = (f[j] - f[j - 1] + mod) % mod ;
                f[j] = 1ll * inv[mod - i] * f[j] % mod ; 
            }
            coef = 1ll * inv[coef] * H[i] % mod ; 
            for(int j = 0; j <= n; j ++) ans[j] = (ans[j] + 1ll * coef * f[j] % mod) % mod ;  
        }
        return ; 
    }
    
    int main() {
     	n = gi() ; K = gi() ; W = gi() ; 
     	for(int i = 1; i <= n; i ++) val[i] = gi() ; 
     	for(int i = 1,u,v; i < n; i ++) u = gi() , v = gi() , AddEdge(u , v) , AddEdge(v , u) ; 
     	Solve(1) ; 
     	for(int i = 1; i <= n + 1; i ++) Solve(i) ; 
     	//for(int i = 1; i <= n + 1; i ++) cout << "H("<<i<<") = " << H[i] << endl ; 
        Lagrange() ; 
     	Ans = 0 ; 
     	for(int j = K; j <= n; j ++) Ans = (Ans + ans[j]) % mod ; 
     	cout << Ans << endl ; 
     	return 0 ; 
    }
    

    所以所谓的整体DP到底是啥啊,根本没看到什么虚树的影子啊?

  • 相关阅读:
    PHP函数之array_chunk
    C#常用的数据结构
    SQLServer锁和并发控制
    数据库堵塞和死锁详解
    SQLServer事务隔离级别
    HTML5 Canvas 为网页添加文字水印
    浏览器记住密码的自动填充Input问题完美解决方案
    C#队列Queue实现一个简单的电商网站秒杀程序
    C# 递归省市区三级树结构
    System.InvalidOperationException:“线程间操作无效: 从不是创建控件“txtPortName02”的线程访问它。”
  • 原文地址:https://www.cnblogs.com/GuessYCB/p/10355766.html
Copyright © 2011-2022 走看看