WC2014-紫荆花之恋

一棵树,每条边有边权,点有点权\(r\)\(n\)次加入一个点,给出它与父亲的连边长度和它的点权,问此时总共有多少对点满足\(r_i+r_j\ge dist(i,j)\)\(n\le 10^5\)

分析

树上点对统计可以使用点分治。每次只需要统计新增了多少对满足条件的点。设一对点\((u,v)\)的lca为\(k\),那么条件可以转化为\(r_u-dist(u,k)\ge dist(v,k)-r_v\),每次新加入点直接挂在父亲下面并直接往上跳,设当前跳到点\(c\),只要统计经过这个点有多少满足条件的即可。我们拿着\(r_u-dist(u,k)\)去查询有多少个\(dist(v,k)-r_v\)小于等于它就好了。用平衡树来维护这个东西,查询完之后就把它插入平衡树里。

但这样明显会重复计算从同一个子树来的点,所以在每个点还要再开一个平衡树,记录一下子树中到这个点的父亲的值,减一下即可。

上面我们说直接把这个点挂在下面,这事实上是很不对的,一个链的情况就可以把这个卡掉。于是我们可以利用重构的思想,如果一个子树的大小大于它父亲子树大小的\(\alpha\),那么就找到最上面一个这样的位置,重构整颗子树,把它变成一个点分治树的样子。替罪羊树的时间复杂度分析说明这样做的复杂度为\(O(f(n)log _{\frac{1}{\alpha}}n)\),其中\(f(n)\)为重构大小为\(n\)的子树的复杂度。

这一题中,点分治树的深度是\(O(logn)\)的,所以一次重构的可以直接每个点往上跳\(O(logn)\)个父亲,每次直接向平衡树中插入,总复杂度为\(O(nlog^3n)\)

代码

弃坑啦!!!!写过splay,treap,奇怪重量平衡树,最后改成替罪羊树来作平衡树,现在uoj上80分

不过学到了一种点分治找中心的神奇写法,超级好写,只要两行~

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long giant;
int read() {
    int x=0,f=1;
    char c=getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
void write(giant x) {
    if (!x) puts("0"); else {
        static char s[25];
        int tot=0;
        while (x) s[++tot]=x%10+'0',x/=10;
        while (tot) putchar(s[tot--]);
        puts("");
    }
}
const int maxn=1e5+1;
const int maxm=5e6;
const int maxj=17;
const double alpha=0.77;
giant ans=0;
int fa[maxn],d[maxn],rec[maxn],size[maxn],my[maxn],onf[maxn],n;
bool able[maxn];
vector<int> g[maxn];
inline int conv(int x) {
    return x^((int)(ans%(giant)1e9));
}
struct graph {
    int h[maxn],tot,f[maxn][maxj],dep[maxn],dis[maxn];
    graph ():tot(0) {
        for (int i=0;i<maxj;++i) f[1][i]=1;
    }
    void add(int u,int v,int w) {
        g[u].push_back(v);
        g[v].push_back(u);
        f[v][0]=u;
        for (int j=1;j<maxj;++j) f[v][j]=f[f[v][j-1]][j-1];
        dep[v]=dep[u]+1,dis[v]=dis[u]+w;
    }
    int lca(int x,int y) {
        if (dep[x]<dep[y]) swap(x,y);
        for (int j=maxj-1;j>=0;--j) if (dep[f[x][j]]>=dep[y]) x=f[x][j];
        if (x==y) return x;
        for (int j=maxj-1;j>=0;--j) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
        return f[x][0];
    }
    int dist(int x,int y) {
        return dis[x]+dis[y]-2*dis[lca(x,y)];
    }
} G;
int aux[maxn];
struct Treap {
    struct node {
        int ch[2],fa,val,size;
    } t[maxm];
    int pool[maxm],per;
    Treap ():per(0) {
        for (int i=1;i<maxm;++i) pool[i]=i;
    }
    inline void update(int x) {
        t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1;
    }
    inline void delnode(int &x) {
        if (!x) return;
        t[x].ch[0]=t[x].ch[1]=t[x].fa=t[x].val=t[x].size=0;
        pool[per--]=x;
        x=0;
    }
    inline int newnode(int x) {
        int nw=pool[++per];
        t[nw].size=1;
        t[nw].val=x;
        return nw;
    }
    bool rson(int x) {
        return t[t[x].fa].ch[1]==x;
    }
    void travel(int x,int &len) {
        if (!x) return;
        travel(t[x].ch[0],len);
        aux[++len]=t[x].val;
        travel(t[x].ch[1],len);
        delnode(x);
    }
    int reb(int l,int r) {
        if (l>r) return 0;
        int mid=l+r>>1;
        int nw=newnode(aux[mid]);
        t[nw].ch[0]=reb(l,mid-1);
        t[nw].ch[1]=reb(mid+1,r);
        update(nw);
        return nw;
    }
    void rebid(int x) {
        int len=0;
        travel(x,len);
        int fat=t[x].fa;
        bool d=rson(x);
        int l=1,r=len;
        t[fat].ch[d]=reb(l,r);
        update(fat);
    }
    inline void insert(int &rt,int x) {
        int nw=newnode(x);
        if (!rt) {
            rt=nw;
            return;
        }
        int now=rt;
        while (true) {
            ++t[now].size;
            int &tmp=t[now].ch[x>t[now].val];
            if (tmp) now=tmp; else {
                tmp=nw;
                break;
            }
        }
        int goat=0;
        for (;t[now].fa;now=t[now].fa) if (t[now].size>0.6*t[t[now].fa].size) goat=now;
        if (goat) rebid(goat);
    }
    inline int le(int now,int x) {
        int ret=0;
        while (now) {
            if (x>t[now].val) ret+=t[t[now].ch[0]].size+1; else
            if (x==t[now].val) ++ret;
            now=t[now].ch[x>t[now].val];
        }
        return ret;
    }
    void clear(int &x) {
        if (x) clear(t[x].ch[0]),clear(t[x].ch[1]);
        delnode(x);
    }
} tree;
inline void addit(int u,int v,int w) {
    G.add(u,v,w);
    fa[v]=u,d[v]=d[u]+1,size[v]=1;
}
inline bool bad(int x) {
    return size[x]>alpha*size[fa[x]];
}
void label(int x,int f,int no) {
    able[x]=true;
    for (int v:g[x]) if (v!=f && d[v]>d[no]) label(v,x,no);
}
int Size(int x,int f) {
    size[x]=1;
    for (int v:g[x]) if (v!=f && able[v]) size[x]+=Size(v,x);
    return size[x];
}
int Root(int x,int f,int hf) {
    for (int v:g[x]) if (v!=f && able[v] && size[v]>hf) return Root(v,x,hf);
    return x;
}
void rebuild(int x,int fat,int up) {
    int sz=Size(x,0),rt=Root(x,0,sz>>1);
    able[rt]=false,d[rt]=d[fa[rt]=fat]+1,size[rt]=size[x];
    tree.clear(my[rt]),tree.clear(onf[rt]);
    for (int v:g[rt]) if (able[v]) rebuild(v,rt,up);
    for (int y=rt;y!=up;y=fa[y]) {
        tree.insert(my[y],G.dist(y,rt)-rec[rt]);
        if (!fa[y]) break;
        tree.insert(onf[y],G.dist(fa[y],rt)-rec[rt]);
    }
}
int cs=0;
inline void work(int x) {
    int goat=0;
    for (int y=x;y;y=fa[y]) {
        int d=G.dist(x,y);
        ans+=tree.le(my[y],rec[x]-d);
        tree.insert(my[y],d-rec[x]);
        if (!fa[y]) break;
        d=G.dist(x,fa[y]);
        ans-=tree.le(onf[y],rec[x]-d);
        tree.insert(onf[y],d-rec[x]);
        ++size[fa[y]];
        if (bad(y)) goat=y;
    }
    if (goat) ++cs,label(fa[goat],0,fa[fa[goat]]),rebuild(fa[goat],fa[fa[goat]],fa[fa[goat]]);
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
    freopen("my.out","w",stdout);
#endif
    srand(200);
    puts("0");
    read(),n=read(),read(),read(),rec[1]=read();
    tree.insert(my[1],-rec[1]),size[1]=1;
    for (int i=2;i<=n;++i) {
//      int x=conv(read());
        int x=read();
        int c=read();
        rec[i]=read();
        addit(x,i,c);
        work(i);
        write(ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/owenyu/p/6810224.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值