zoukankan      html  css  js  c++  java
  • 树上启发式合并(dsu on tree)合集

    1.Codeforces Edu Round 2. E. Lomsat gelral

    题意:给你一棵树,树上有n个节点,每个节点有自己的颜色,让你求每个节点子树出现次数最多的颜色之和。

    题解思路:利用轻重链剖分的特性去枚举每个节点,再用数组记录一下当前节点子树上每种颜色出现的次数,最后计算答案即可。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> PII;
    #define ls l,mid,rt<<1
    #define rs mid+1,r,rt<<1|1
    const int MAXN = 1e5+10;
    const double EPS = 1e-12;
    const ll mod = 1e9+7;
    
    int n;
    int sz[MAXN],wson[MAXN],c[MAXN],csum[MAXN],b[MAXN];
    ll ans[MAXN],maxx,COL;
    bool vis[MAXN];
    vector<int>G[MAXN];
    
    void dfs(int u,int fa){
        sz[u]=1;
        for(auto v:G[u]){
            if(v==fa)continue;
            dfs(v,u);
            sz[u]+=sz[v];
            if(sz[v]>sz[wson[u]])wson[u]=v;
        }
    }
    
    void add(int u,int fa){
        csum[c[u]]++;
        if(maxx<csum[c[u]])maxx=csum[c[u]],COL=c[u];
        else if(maxx==csum[c[u]])COL+=c[u];
        for(auto v:G[u]){
            if(v==fa||vis[v])continue;
            add(v,u);
        }
    }
    
    void del(int u,int fa){
        csum[c[u]]--;
        for(auto v:G[u]){
            if(v==fa)continue;
            del(v,u);
        }
    }
    
    void DFS(int u,int fa,int ty){
        for(auto v:G[u]){
            if(v==fa||v==wson[u])continue;
            DFS(v,u,0);
        }
        if(wson[u])DFS(wson[u],u,1),vis[wson[u]]=1;
        add(u,fa);
        ans[u]=COL;
        vis[wson[u]]=0;
        if(ty==0)del(u,fa),maxx=0,COL=0;
    }
    
    void solve(int cas){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&c[i]);
        for(int i=1,u,v;i<n;i++){
            scanf("%d %d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1,0);
        DFS(1,0,1);
        for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
    }
    
    int main()
    {
        int T=1;
        //scanf("%d",&T);
        for(int i=1;i<=T;i++)solve(i);
    }

    2.2020 CCPC Wannafly Winter Camp Day2  E.阔力梯的树

    题意:题目说的很清楚,自己看

    题解思路:题目里只有对子树的询问,很显然是树上启发式合并,但由于中间有个排序过程,priority_queue不能中间插值,这里可以使用set来计算。注意,题目中不能等到把所有子树节点插入set后再进行求值(n^2logn显然会t),边插入就可以边计算了,注意消去前后影响就好。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> PII;
    #define ls l,mid,rt<<1
    #define rs mid+1,r,rt<<1|1
    #define endl '
    '
    #define ps puts("###")
    const int MAXN = 1e5+10;
    const double EPS = 1e-12;
    const ll mod = 1e9+7;
    
    int n;
    int sz[MAXN],wson[MAXN],vis[MAXN];
    ll ans[MAXN],sum;
    vector<int>G[MAXN];
    set<int>s;
    
    void dfs(int u){
        sz[u]=1;wson[u]=0;
        for(auto v:G[u]){
            dfs(v);
            sz[u]+=sz[v];
            if(sz[v]>sz[wson[u]])wson[u]=v;
        }
    }
    
    void add(int u){
        s.insert(u);
        if(s.size()==1)return ;
        else{
            auto it1=s.lower_bound(u),it2=s.upper_bound(u);
            if(it1==s.begin())sum+=1ll*(*it2-*it1)*(*it2-*it1);
            else if(it2==s.end()){
                --it1;
                --it2;
                sum+=1ll*(*it2-*it1)*(*it2-*it1);
            }
            else {
                --it1;
                sum-=1ll*(*it2-*it1)*(*it2-*it1);
                --it2;
                sum+=1ll*(*it2-*it1)*(*it2-*it1);
                ++it1;++it2;
                sum+=1ll*(*it2-*it1)*(*it2-*it1);
            }
        }
    }
    
    void dfs3(int u){
        add(u);
        for(auto v:G[u]){
            if(!vis[v])dfs3(v);
        }
    }
    
    void dfs2(int u,int ty){
        for(auto v:G[u]){
            if(v!=wson[u])dfs2(v,0);
        }
        if(wson[u])dfs2(wson[u],1),vis[wson[u]]=1;
        dfs3(u);
        ans[u]=sum;
        vis[wson[u]]=0;
        if(!ty)s.clear(),sum=0;
    }
    
    void solve(int cas){
        scanf("%d",&n);
        for(int i=2,fa;i<=n;i++){
            scanf("%d",&fa);
            G[fa].push_back(i);
        }
        dfs(1);dfs2(1,1);
        for(int i=1;i<=n;i++)printf("%lld
    ",ans[i]);
    }
    
    int main()
    {
        int T=1;
        //scanf("%d",&T);
        for(int i=1;i<=T;i++)solve(i);
    }

     


    3.浙江农林大学第十九届程序设计竞赛  J.Tree

    题意:题目里说的很清楚

    题解思路:很显然,这题是利用dsu on tree的优越性去枚举当前点作为lca点时符合条件的点对数量,那么怎么才能不重不漏地枚举呢?根据lca的特性,显然只有位于不同第一层子树下的点lca会在当前点上,这时我们可以利用map(或是可以离散一下)去对当前子树节点的每个值进行记录,能形成多少对其实就是sum1[x]*sum2[2 * root - x],其中,sum1代表的是当前子树每个值的数量,sum2代表的是其他子树每个值的数量,root代表当前枚举到作为lca的点。最后把ans*2即可(x,y位置互换)。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> PII;
    #define ls l,mid,rt<<1
    #define rs mid+1,r,rt<<1|1
    #define endl '
    '
    #define ps puts("###")
    const int MAXN = 1e5+10;
    const double EPS = 1e-12;
    const ll mod = 1e9+7;
    
    int n;
    int a[MAXN],sz[MAXN],wson[MAXN],vis[MAXN];
    ll ans;
    vector<int>G[MAXN];
    map<int,int>mp;
    void dfs(int u,int fa){
        sz[u]=1;
        for(auto v:G[u]){
            if(v==fa)continue;
            dfs(v,u);
            sz[u]+=sz[v];
            if(sz[v]>sz[wson[u]])wson[u]=v;
        }
    }
    
    void add(int u,int fa){
        mp[a[u]]++;
        for(auto v:G[u]){
            if(v==fa||vis[v])continue;
            add(v,u);
        }
    }
    
    void calc(int u,int fa,int rt){
        if(2*a[rt]>a[u])ans+=mp[2*a[rt]-a[u]];
        for(auto v:G[u]){
            if(v==fa||vis[v])continue;
            calc(v,u,rt);
        }
    }
    
    void dfs2(int u,int fa,int ty){
        for(auto v:G[u]){
            if(v==fa||v==wson[u])continue;
            dfs2(v,u,0);
        }
        if(wson[u])dfs2(wson[u],u,1),vis[wson[u]]=1;
        for(auto v:G[u]){
            if(v==fa||vis[v])continue;
            calc(v,u,u);
            add(v,u);
        }
        mp[a[u]]++;
        vis[wson[u]]=0;
        if(ty==0)mp.clear();
    }
    
    void solve(int cas){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1,u,v;i<n;i++){
            scanf("%d %d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1,0);
        dfs2(1,0,1);
        cout<<ans*2;
    }
    
    int main()
    {
        int T=1;
        //scanf("%d",&T);
        for(int i=1;i<=T;i++)solve(i);
    }

    4.2020 CCPC 长春  F. Strange Memory

    题意:给你一棵树,树上有n个节点,每个节点有自己的值,求以下式子的值

      

    题解思路:和上一题一样,枚举每个点作为lca点,再去算答案,由于答案是a^b1+a^b2+...+a^bn的形式,注意a^b+a^c != a^(b+c),我们这里需要对b1、b2...bn进行拆位操作,用数组记录一下这个val对应的key每一位上有多少个0和1,最后统计答案时只需要把贡献都加起来就行了。(有一说一,轻重链剖分在不修改的情况下是真好用。)

    #include<bits/stdc++.h>
    #include<tr1/unordered_map>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> PII;
    #define ls l,mid,rt<<1
    #define rs mid+1,r,rt<<1|1
    #define endl '
    '
    #define ps puts("###")
    const int MAXN = 1e5+10;
    const double EPS = 1e-12;
    const ll mod = 1e9+7;
    tr1::unordered_map<int,int>mp;
    
    int n,m;
    int a[MAXN],sz[MAXN],wson[MAXN],sum[MAXN][22][2];
    ll ans;
    vector<int>G[MAXN];
    
    void dfs1(int u,int fa){
        sz[u]=1;
        for(auto v:G[u]){
            if(v==fa)continue;
            dfs1(v,u);
            sz[u]+=sz[v];
            if(sz[v]>sz[wson[u]])wson[u]=v;
        }
    }
    
    void calc(int u,int fa,int rt){
        int x=a[u]^a[rt];
        if(mp.count(x)){
            int uu=u,now=0;
            while(now<22){
                int xx=uu%2;
                ans+=1ll*sum[mp[x]][now][xx^1]*(1ll<<now);
                uu/=2;
                now++;
            }
            //cout<<"#######"<<u<<" "<<a[u]<<" "<<x<<" "<<a[rt]<<" "<<ans<<endl;
        }
        for(auto v:G[u]){
            if(v==fa)continue;
            calc(v,u,rt);
        }
    }
    
    void update(int u,int fa,int val){
        int uu=u,now=0;
        while(now<22){
            int xx=uu%2;
            sum[mp[a[u]]][now][xx]+=val;
            uu/=2;
            now++;
        }
        for(auto v:G[u]){
            if(v==fa)continue;
            update(v,u,val);
        }
    }
    
    void dfs2(int u,int fa,int ty){
        for(auto v:G[u]){
            if(v==fa||v==wson[u])continue;
            dfs2(v,u,0);
        }
        if(wson[u])dfs2(wson[u],u,1);
        for(auto v:G[u]){
            if(v==fa||v==wson[u])continue;
            calc(v,u,u);
            update(v,u,1);
        }
        int uu=u,now=0;
        while(now<22){
            int xx=uu%2;
            sum[mp[a[u]]][now][xx]++;
            uu/=2;
            now++;
        }
        if(ty==0)update(u,fa,-1);
    }
    
    int main()
    {
        scanf("%d",&n);
        int cnt=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            if(!mp[a[i]])mp[a[i]]=++cnt;
        }
        for(int i=1,u,v;i<n;i++){
            scanf("%d %d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs1(1,0);dfs2(1,0,1);
        cout<<ans<<endl;
    }
  • 相关阅读:
    Neko's loop HDU-6444(网络赛1007)
    Parameters
    SETLOCAL
    RD / RMDIR Command
    devenv 命令用法
    Cannot determine the location of the VS Common Tools folder.
    'DEVENV' is not recognized as an internal or external command,
    How to change Visual Studio default environment setting
    error signing assembly unknown error
    What is the Xcopy Command?:
  • 原文地址:https://www.cnblogs.com/Mmasker/p/13954126.html
Copyright © 2011-2022 走看看