[WC2014]紫荆花之恋

题意:每次插入一个点,询问 dis(i,j)ri+rj d i s ( i , j ) ≤ r i + r j 的数量

①:假装这棵树是一开始给你的

树上路径问题 > − > 点分治

考虑重心 u u ,答案就是dis(i,u)+dis(u,j)ri+rj的数量
移一个项 dis(i,u)rirjdis(u,j) ⇒ d i s ( i , u ) − r i ≤ r j − d i s ( u , j )

把所有的 dis(i,u)ri d i s ( i , u ) − r i 插入 u u 的平衡树,询问u的平衡树中 rjdis(u,j) r j − d i s ( u , j ) 的排名就好了

计算贡献什么的基本都是点分治的基本套路(其实主要是这部分大家都讲得很清楚了)

②:这棵树会动怎么做?

考虑用按照替罪羊树的思想,从这个点不断跳上一层重心,当满足 Szi>Szfaiα S z i > S z f a i ∗ α 时就重构 fai f a i 这棵树,当然 fai f a i 要是最浅的

③:细节&卡常

听起来似乎好简单(想完题解后我也是这么想的),但是写起来写到怀疑人生

我们讲一讲这道题最要命的地方——细节&卡常

1. 1. 你需要一颗高效的平衡树,可能你 8090 80 − 90 分,这时候换一颗平衡树说不定就卡过了;如果你用 Treap T r e a p 的话不如手写随机会快一些

2. 2. 你需要一个平衡树的垃圾桶,因为这题重构是要清空这棵树以及这棵树上每个节点的平衡树的,这里会造成巨大的空间浪费(其实你不写垃圾桶你可能会被卡到 MLE M L E )

3. 3. 我稍微感受了一下平衡树写法的速度大概是这样的:结构体 > > 指针 > > 数组

其实想想也比较好理解吧

4. 4. 对于像我一样的手残党,一开始写代码的时候最好还是写到 namespace n a m e s p a c e 里面吧,之前因为平衡树节点的 size s i z e sz s z 当名字,然后点分治按照习惯也写的 sz s z ,后来发现重名了,然后就稍微改了一下,然后有一个地方没有改,但是他小数据跑出来了 exm?!?! e x m ? ! ? ! ,然后调到绝望才找出来.什么你说那样不好看 ? ? 你不知道先写完保证不错然后ctrl+F替换吗?

5. 5. 什么你问我为什么第一名那么快 ? ? 其实主要是处理链的部分非常快

因为我们可以发现链完全不要像上面那么做

假设1是链的顶端,左右分别拉一条链,令 di=dis(1,i) d i = d i s ( 1 , i )

考虑新加进来的点一定在链的底端,考虑两种情况

dudiru+ridirirudu,u d u − d i ≤ r u + r i ⇒ − d i − r i ≤ r u − d u , u i i 在同一边

du+diru+ridirirudu,u i i 在不同边

所以你维护4棵平衡树就可以在 O(nlogn) O ( n log ⁡ n ) 的时间内处理出答案了

注意要特判当 dur1+ru d u ≤ r 1 + r u 时答案要减 1 1

6.卡常有风险, code c o d e 需谨慎

7. 7. 什么你说还不够快 ? ? 那你可以用bfs来求重心,不要用 STL S T L ,能放在一块的用结构体存起

至于代码的话将就着看吧

#include<bits/stdc++.h>
#define fp(i,a,b) for(int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(int i=a,I=b-1;i>I;--i)
#define go(i,u) for(int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
    char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
    while(c=gc(),47<c&&c<58)x=x*10+c-48;x*=y;
}
char sr[1<<21],z[20];int C=-1,Z;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
template<class T>inline void we(T x){
    if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=1<<17,L=30,M=35*N,P=1e9;
const double alpha=0.777,beta=0.77;
typedef int arr[N];
typedef long long ll;
struct eg{int nx,to,w;}e[2*N];
int n,ce;arr R,fi;ll ans;
inline void add(int u,int v,int w){e[++ce]=eg{fi[u],v,w},fi[u]=ce;}
//Scapegoat Tree
struct sgt{int sz,w,c[2];}tr[M];int cn,cb,bin[M];
inline int New(){return cb?bin[cb--]:++cn;}
inline void up(int u){tr[u].sz=tr[tr[u].c[0]].sz+tr[tr[u].c[1]].sz+1;}
inline void rot(int&u,int k){int v=tr[u].c[k];tr[u].c[k]=tr[v].c[!k],tr[v].c[!k]=u,up(u),up(u=v);}
void ins(int&u,int w){
    if(!u)return tr[u=New()]=sgt{1,w,{0,0}},void();
    bool k=w>tr[u].w;++tr[u].sz;ins(tr[u].c[k],w);if(tr[tr[u].c[k]].sz>tr[u].sz*beta)rot(u,k);
}
inline int rk(int u,int w){if(!u)return 0;return w<tr[u].w?rk(tr[u].c[0],w):(tr[tr[u].c[0]].sz+1+rk(tr[u].c[1],w));}
void cle(int&u){if(!u)return;bin[++cb]=u;cle(tr[u].c[0]);cle(tr[u].c[1]);u=0;}
//Dynamic Divide
struct dyd{int d,f[L],l[L];}g[N];
int rt,Sum,T[N][2];arr q,d,Sz,Fa,fx;bool vis[N];vector<int>Tr[N];
void gr(int u,int fa){
    fx[u]=0,Sz[u]=1;
    go(i,u)if(!vis[v]&&v^fa)gr(v,u),Sz[u]+=Sz[v],cmax(fx[u],Sz[v]);
    cmax(fx[u],Sum-Sz[u]);if(fx[u]<fx[rt])rt=u;
}
inline void bfs(int u){
    int h=1,t=1;q[t]=u;
    while(h<=t){vis[u=q[h++]]=1;go(i,u)if(!vis[v])d[q[++t]=v]=d[u]+e[i].w;}
    q[0]=t;fp(i,1,t)vis[q[i]]=0;
}
inline void pb(int u,int f,int w){dyd&d=g[u];d.f[d.d]=f,d.l[d.d]=w,++d.d;}
void sol(int u){
    vis[u]=1;ins(T[u][0],-R[u]);Tr[u].push_back(u),pb(u,u,0);
    go(i,u)if(!vis[v]){
        d[v]=e[i].w,bfs(v),Sum=Sz[v],gr(v,rt=0);
        fp(j,1,q[0])Tr[u].push_back(v=q[j]),ins(T[u][0],d[v]-R[v]),ins(T[rt][1],d[v]-R[v]),pb(v,u,d[v]);
        Fa[rt]=u,sol(rt);
    }
}
inline void Re(int u){
    int*a=Tr[u].data(),NewSiz=g[Fa[u]].d,v;
    fp(i,1,Tr[u].size()-1){
        vis[v=a[i]]=0,Tr[v].clear();
        cle(T[v][0]),cle(T[v][1]);
        g[v].d=NewSiz;
    }
    Sum=Tr[u].size(),gr(u,rt=vis[u]=0);
    T[rt][1]=T[u][1];cle(T[u][0]);T[u][1]=0;
    Tr[u].clear();g[u].d=NewSiz;Fa[rt]=Fa[u];sol(rt);
}
inline void calc(int u,int f,int w){
    add(u,f,w),add(f,u,w),Fa[u]=f;g[u]=g[f];
    vis[u]=1;pb(u,u,-w);int*a=g[u].f,*b=g[u].l,p;
    fp(i,0,g[u].d-1){
        b[i]+=w;Tr[p=a[i]].push_back(u);
        ans+=rk(T[p][0],R[u]-b[i])-(i?rk(T[p][1],R[u]-b[i-1]):0);
        ins(T[p][0],b[i]-R[u]);if(i)ins(T[p][1],b[i-1]-R[u]);
    }we(ans);
    fp(i,1,g[u].d-1)if(tr[T[a[i]][0]].sz>tr[T[a[i-1]][0]].sz*alpha)return Re(a[i-1]);
}
int a,b,wic,t[2][2],Chain[2]={1,1};
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    sd(n),sd(n);int u=2;fx[0]=N,we(0);
    sd(a),sd(b),sd(R[1]);fp(i,0,1)fp(j,0,1)ins(t[i][j],-R[1]);
    for(;u<=n;++u){
        sd(a),sd(b),sd(R[u]);
        #ifdef ONLINE_JUDGE
            a^=ans%P;
        #endif
        if(a^Chain[0]&&a^Chain[1])break;
        Fa[u]=a;add(u,a,b),add(a,u,b);
        wic=Fa[u]==Chain[1],Chain[wic]=u;d[u]=d[a]+b;
        ans+=rk(t[0][wic],R[u]-d[u])+rk(t[1][!wic],R[u]-d[u]);
        ins(t[0][wic],-d[u]-R[u]);ins(t[1][wic],d[u]-R[u]);
        if(d[u]<=R[1]+R[u])--ans;we(ans);
    }if(u<=n){
        fp(i,0,1)fp(j,0,1)cle(t[i][j]);
        Sum=u-1;gr(1,0);Fa[rt]=0;sol(rt);calc(u,a,b);
        for(++u;u<=n;++u){
            sd(a),sd(b),sd(R[u]);
            #ifdef ONLINE_JUDGE
                a^=ans%P;
            #endif
            calc(u,a,b);
        }
    }
return Ot(),0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值