dsu on tree

dsu on tree

在处理子树询问时,子树与子树之间总是相互干扰.   

常用的处理手法有线段树合并,dfs 序等来规避子树之间的干扰. 

dsu on tree 则是利用的树链剖分轻重链的思想在多一个 $O(\log n)$ 的复杂度下避开子树之间干扰

算法模板: 

void dfs2(int x, int ff, int op) {
    for(int i=0;i<G[x].size();++i) {
        int v=G[x][i]; 
        if(v==ff||v==son[x]) continue;  
        // 轻儿子不统计进去. 
        dfs2(v, x, 0); 
    }
    if(son[x]) {
        // 重儿子统计进去. 
        dfs2(son[x], x, 1);    
    } 
    Son=son[x];  
    // 再将轻儿子统计进去,并计算答案
    calc(x, ff);  
    ans[x]=d;  
    Son=0;      
    if(op==0) mx=0,d=0,dele(x, ff);  
}

  

时间复杂度分析: 

每个点会在 dfs 的时候被访问一遍,为 $O(n).$  

一个点被 $\mathrm{calc}$ 函数访问,当且仅当祖先有轻边,访问次数为轻边次数 $O(\log n)$.   

一个点被 $\mathrm{delete}$ 函数访问,也需要祖先有轻边,访问次数同样是 $O(\log n)$.  

那么,所有点访问的总时间复杂度就是 $O(n \log n)$.  

特别注意:在 $\mathrm{delete}$ 的时候删除所有信息就行,不必担心正在访问其他点之类的问题. 

 

Escape Through Leaf

来源:CF932F

写一下 DP 式子发现是 $\mathrm{dp[x]=dp[y]+a[x] \times b[y]}$. 

对于子树中的 $\mathrm{y}$,对祖先每一个 $\mathrm{dp[x]}$ 的贡献是关于 $\mathrm{a[x]}$ 的一次函数.  

如果只有一次子树询问显然可以用李超线段树来做.  

由于每一个点都要求解答案,故采用 $\mathrm{DSU}$ 的方式进行求解,时间复杂度为 $O(n \log^2 n)$.  

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm> 
#define ll long long 
#define pb push_back 
#define ls now<<1 
#define rs now<<1|1
#define N  200009 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
const int inf=100003;  
const ll  MA = 1ll<<50;   
struct Line {
    ll k,b;      
}line[N<<2];  
vector<int>G[N];  
ll dp[N]; 
int cn,n,a[N],b[N],size[N],son[N];   
ll calc(Line o,int pos) {
    return 1ll*o.k*pos+o.b;  
}
struct SGT {
    int tree[N<<2];       
    vector<int>clr;  
    void update(int l,int r,int now,int L,int R,int x) {
        int mid=(l+r)>>1;      
        if(!tree[now]) tree[now]=x,clr.pb(now);      
        else {
            if(calc(line[tree[now]], mid) > calc(line[x], mid))   
                swap(tree[now], x);  
            if(calc(line[x], l) < calc(line[tree[now]], l) && l!=r) 
                update(l, mid, ls, L, R, x); 
            if(calc(line[x], r) < calc(line[tree[now]], r) && l!=r)  
                update(mid+1,r,rs,L,R,x);  
        }
    }
    ll query(int l,int r,int now,int p) {     
        if(l==r) {
            return tree[now] ? calc(line[tree[now]], l) : MA; 
        }
        int mid = (l + r) >> 1;  
        ll re=MA;  
        if(tree[now]) re=min(re, calc(line[tree[now]], p));  
        if(p<=mid)   return min(re, query(l,mid,ls,p)); 
        else return min(re, query(mid+1,r,rs,p));  
    }
    void CLR() {
        for(int i=0;i<clr.size();++i)     
            tree[clr[i]]=0; 
        clr.clear(); 
    }
}T;    
int Son;  
void upd(int x, int ff) {
    // y = b[x] ( ) + dp[x]    
    ++cn;  
    line[cn].k=b[x]; 
    line[cn].b=dp[x];  
    T.update(-inf, inf, 1, -inf, inf, cn);   
    for(int i=0;i<G[x].size();++i) {
        int v=G[x][i]; 
        if(v==ff) continue;  
        upd(v, x);  
    }
}
void dfs1(int x, int ff) {
    size[x]=1,son[x]=0; 
    for(int i=0;i<G[x].size();++i) {
        int v=G[x][i]; 
        if(v==ff) continue;  
        dfs1(v, x); 
        size[x]+=size[v]; 
        if(size[v]>size[son[x]]) 
            son[x]=v;  
    }
}
void dfs2(int x, int ff, int op) {
    for(int i=0;i<G[x].size();++i) {
        int v=G[x][i];  
        if(v==ff||v==son[x]) continue;  
        dfs2(v, x, 0); 
        // 不计子树影响.  
    }
    // 计算重儿子影响.  
    if(son[x]) dfs2(son[x], x, 1); 
    // 算进轻儿子影响.   
    for(int i=0;i<G[x].size();++i) {
        int v=G[x][i]; 
        if(v==ff||v==son[x]) continue;  
        upd(v, x);  
    }  
    // 计算当前点答案.  
    if(size[x]==1) dp[x]=0;  
    else {
        dp[x]=T.query(-inf, inf, 1, a[x]);   
    }
    ++cn; 
    line[cn].k=b[x]; 
    line[cn].b=dp[x]; 
    T.update(-inf, inf, 1, -inf, inf, cn);  
    // 需要清除.
    if(op==0) T.CLR(),cn=0;      
}
int main() { 
    // setIO("input");       
    scanf("%d",&n); 
    for(int i=1;i<=n;++i) scanf("%d",&a[i]); 
    for(int i=1;i<=n;++i) scanf("%d",&b[i]);  
    for(int i=1;i<n;++i) {
        int x,y; 
        scanf("%d%d",&x,&y); 
        G[x].pb(y); 
        G[y].pb(x); 
    }
    dfs1(1, 0); 
    dfs2(1, 0, 1);   
    for(int i=1;i<=n;++i) {
        printf("%lld ",dp[i]);   
    }
    return 0; 
}

  

 

posted @ 2021-09-07 19:07  guangheli  阅读(102)  评论(0)    收藏  举报