CSU 1811 Tree Intersection(平衡树的子树合并)

Posted DarkTong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSU 1811 Tree Intersection(平衡树的子树合并)相关的知识,希望对你有一定的参考价值。

题意:对于每一条边,去掉一条边后,生成两颗树,问这两颗树的交集大小。

分析:1、O(n^2)算法:每次都用O(n)的时间去合并两个区间,因此可以从合并这个地方去优化。

        2、可以这么考虑,在遍历树的时候计算去掉的两树的交集,那么每次我们只要把当前点的所有子树合并便可得到当前点的父边的解。

        3、具体实现细节代码见。

复杂度是O(nlog^2n),具体可以参考大白p234那一道题。

吐槽:手挫,思路一早就有了,就代码一直写得….

/************************************************
Author        :DarkTong
Created Time  :2016/9/5 21:20:03
File Name     :Hulan_I.cpp
*************************************************/

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const int maxn = 200000 + 100;
#define MIN 0x80000000
#define MAX 0x7fffffff
int vis[maxn];
struct Splay_Tree
{
    int ch[maxn][2], r[maxn], val[maxn], s[maxn], cnt[maxn];
    queue<int> ssz;
    void init(){
        while(!ssz.empty()) ssz.pop();
        for(int i=1;i<maxn;++i) ssz.push(i);
    }
    int newNode(int vv=0){
        int sz = ssz.front(); ssz.pop();
        ch[sz][0]=ch[sz][1]=0, s[sz]=1;
        val[sz]=vv; r[sz]=rand(); 
        cnt[sz]=0;
        return sz;
    }
    //比较函数
    int cmp(int v, int x){
        if(x==v) return -1;
        else return x < v ? 0 : 1;
    }
    //更新s[]
    void maintain(int o){
        s[o] = s[ch[o][0]]+s[ch[o][1]]+1;    
    }
    //旋转操作
    void rotate(int &o, int d){
        int k=ch[o][d^1]; ch[o][d^1]=ch[k][d]; ch[k][d]=o;
        maintain(o);// maintain(k); 
        o=k;
    }
    void insert(int &o, int x, int cn, int &ans){
        if(!o) {
            o=newNode(x);
            cnt[o] = cn;
            if(cn<vis[x]) ++ans;
        }else{
            int d = cmp(val[o], x);    //若有相同值,则不能使用cmp()函数
            if(d!=-1)
            {
                insert(ch[o][d], x, cn, ans);
                if(r[ch[o][d]] > r[o]) rotate(o, d^1);
            }
            else
            {
                cnt[o]+=cn;
                if(cnt[o]==vis[x]) --ans;
            }
        }
        maintain(o);
    }
    void mergeto(int &src, int &dest, int &ans)
    {
        if(ch[src][0]) mergeto(ch[src][0], dest, ans);
        if(ch[src][1]) mergeto(ch[src][1], dest, ans);
        ssz.push(src);
        insert(dest, val[src], cnt[src], ans);
        src = 0;
    }
}slt;


vector<int> G[maxn];
int ans[maxn], val[maxn], n, ha[maxn];
map<pair<int, int>, int> id;
int dfs(int u, int fa)
{
    int tmp, sam = 0;
    slt.insert(ha[u], val[u], 1, sam);
    for(int i=0;i<G[u].size();++i)
    {
        int v = G[u][i];
        if(v==fa) continue;
        int tsam = dfs(v, u);

        if(slt.s[ha[v]] > slt.s[ha[u]])
        {
            swap(ha[u], ha[v]);
            sam = tsam;
        }
        if(ha[v]) slt.mergeto(ha[v], ha[u], sam);
    }
    ans[id[make_pair(min(u, fa), max(u, fa))]] = sam;
    return sam;
}

int main()
{
    int T, cas=1;
    while(scanf("%d", &n)==1)
    {
        memset(vis, 0, sizeof(vis));
        memset(ans, 0, sizeof(ans));
        memset(ha, 0, sizeof(ha));
        slt.init();
        for(int i=1;i<maxn;++i)  G[i].clear();
        id.clear();

        int u, v;
        for(int i=1;i<=n;++i) scanf("%d", &val[i]), vis[val[i]]++;
        for(int i=1;i<n;++i)
        {
            scanf("%d%d", &u, &v);
            if(u>v) swap(u, v);
            id[make_pair(u, v)] = i;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        id[make_pair(0, 1)] = 0;
        dfs(1, 0);
        for(int i=1;i<n;++i)
        {
            printf("%d\n", ans[i]);
        }
    }
    
    return 0;
}

以上是关于CSU 1811 Tree Intersection(平衡树的子树合并)的主要内容,如果未能解决你的问题,请参考以下文章

CSU1811: Tree Intersection

CSU 1811 Tree Intersection(平衡树的子树合并)

CSU 1663: Tree(树链剖分)

CSU 1974: 神奇药水

CSU-2116 Polyline Simplification

CSU-2034 Column Addition