zoukankan      html  css  js  c++  java
  • bzoj3435 [Wc2014]紫荆花之恋(动态点分治+替罪羊树)

    传送门(权限)

    传送门(非权限)

    题解

      我终终终终终终于做出来啦!!!

      作为一个没有学过替罪羊树的蒟蒻现场学了一下替罪羊树,作为一个平衡树都写数组版本的看着大佬的指针题解无语只能硬去理解然后照着抄了一波指针

      然后怎么做呢?

      先把题设式子变形一下$$dist(i,j)leq r_i+r_j$$

      $$dist(i,LCA)+dist(LCA,j)leq r_i+r_j$$

      $$r_i-dist(i,LCA)geq dist(j,LCA)-r_j$$

      然后我们在每一个点开两棵平衡树,分别维护以$i$为根的子树中$dist(i,u)-r_u$和$dist(fa[i],u)-r_u$的值。然后每一次跳点分树时,记录$r_i-dist(i,LCA)+1$,在平衡树里查询有多少个数小于它就好了,修改直接往上跳,不断改

      然而如果原树是一条链怎么办?强制在线,必然会被卡成$O(n^2)$,怎么办?

      我们联想一下替罪羊树的思想,如果点分树上某一个点的子树过大,直接拍扁重建。联想一下替罪羊树,可以发现时间复杂度是能得到保证的,这样可以保证时间复杂度是$O(nlogn)$。

      因为蒟蒻是第一次写替罪羊树&&第一次码这么长的代码,于是加了一堆注释,米娜应该能够看懂吧……

      1 // luogu-judger-enable-o2
      2 //minamoto
      3 #include<cstdio>
      4 #include<cstring>
      5 #include<iostream>
      6 #include<vector>
      7 #define ll long long
      8 #define inf 1000000000
      9 #define N 100005
     10 #define alpha 0.755
     11 using namespace std;
     12 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
     13 char buf[1<<21],*p1=buf,*p2=buf;
     14 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
     15 inline int read(){
     16     #define num ch-'0'
     17     char ch;bool flag=0;int res;
     18     while(!isdigit(ch=getc()))
     19     (ch=='-')&&(flag=true);
     20     for(res=num;isdigit(ch=getc());res=res*10+num);
     21     (flag)&&(res=-res);
     22     #undef num
     23     return res;
     24 }
     25 char sr[1<<21],z[20];int C=-1,Z;
     26 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
     27 inline void print(ll x){
     28     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
     29     while(z[++Z]=x%10+48,x/=10);
     30     while(sr[++C]=z[Z],--Z);sr[++C]='
    ';
     31 }
     32 int n,e,head[N],Next[N<<1],ver[N<<1],val[N];
     33 inline void add(int u,int v){
     34     //加边,构建原树 
     35     ver[++e]=v,Next[e]=head[u],head[u]=e;
     36     ver[++e]=u,Next[e]=head[v],head[v]=e;
     37 }
     38 vector<int> to[N];
     39 int f[N][18],bin[25],tp,dep[N],len[N];
     40 inline int LCA(int a,int b){
     41     if(dep[a]<dep[b]) a^=b^=a^=b;
     42     int i,cha=dep[a]-dep[b];
     43     for(i=tp;~i;--i) if(cha&bin[i]) a=f[a][i];
     44     if(a==b) return a;
     45     for(i=tp;~i;--i) if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
     46     return f[a][0];
     47 }
     48 inline int dis(int a,int b){return len[a]+len[b]-(len[LCA(a,b)]<<1);}
     49 struct Goat{
     50     int val,sz;Goat *ch[2];
     51     Goat(){}
     52     inline bool bad(){
     53         //判断是否某个子树过大 
     54         return ch[0]->sz>=sz*alpha+5||ch[1]->sz>=sz*alpha+5;
     55     }
     56 }*tree1[N],*tree2[N],mem[N<<8],*pool[N<<8],*null,*sta[N];
     57 int tot,top;
     58 void init(){
     59     //构建内存池,避免动态开点时间复杂度太大
     60     null=new Goat(); 
     61     null->ch[0]=null->ch[1]=null,null->val=null->sz=0;
     62     for(int i=0;i<(N<<8);++i) pool[i]=mem+i;
     63     tot=(N<<8)-1;
     64     for(int i=0;i<=n;++i) tree1[i]=tree2[i]=null;
     65 }
     66 Goat** insert(Goat *&a,int val){
     67     //插入节点,并判断是否有子树过大
     68     //注意要开引用 
     69     if(a==null){
     70         a=pool[tot--],a->ch[0]=a->ch[1]=null;
     71         a->val=val,a->sz=1;return &null;
     72     }
     73     ++a->sz;
     74     //小于等于往左插,大于往右插 
     75     Goat **o=insert(a->ch[a->val<val],val);
     76     if(a->bad()) o=&a;return o;
     77 }
     78 int getrk(Goat *o,int val){
     79     //查找有多少比val小的数 
     80     if(o==null) return 0;
     81     return (o->val>=val)?getrk(o->ch[0],val):(getrk(o->ch[1],val)+o->ch[0]->sz+1);
     82 }
     83 void Erholung(Goat *o){
     84     //清除节点,回收内存池 
     85     if(o==null) return;
     86     if(o->ch[0]!=null) Erholung(o->ch[0]);
     87     pool[++tot]=o;
     88     if(o->ch[1]!=null) Erholung(o->ch[1]);
     89 }
     90 void travel(Goat *o){
     91     //暴力重构整棵树(递归找节点) 
     92     if(o==null) return;
     93     if(o->ch[0]!=null) travel(o->ch[0]);
     94     sta[++top]=o;
     95     if(o->ch[1]!=null) travel(o->ch[1]);
     96 }
     97 Goat* build(int l,int r){
     98     //重构 
     99     if(l>r) return null;
    100     int mid=l+r>>1;
    101     Goat *o=sta[mid];o->sz=r-l+1;
    102     o->ch[0]=build(l,mid-1),o->ch[1]=build(mid+1,r);
    103     return o;
    104 }
    105 inline void rebuild(Goat *&o){top=0,travel(o),o=build(1,top);};
    106 inline void Insert(Goat *&a,int val){
    107     //同,这里和上面rebuild也要开引用
    108     Goat **o=insert(a,val);
    109     if(*o!=null) rebuild(*o);
    110 }
    111 int sz[N],son[N],size,rt,fa[N];bool vis[N];
    112 void findrt(int u,int fa){
    113     sz[u]=1,son[u]=0;
    114     for(int i=head[u];i;i=Next[i]){
    115         int v=ver[i];
    116         if(v!=fa&&!vis[v]){
    117             findrt(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]);
    118         }
    119     }
    120     cmax(son[u],size-sz[u]);
    121     if(son[u]<son[rt]) rt=u;
    122 }
    123 void dfs(int u,int f,int rt){
    124     //遍历子树,把所有的东西都插到平衡树里 
    125     Insert(tree1[rt],dis(u,rt)-val[u]);
    126     if(fa[rt]) Insert(tree2[rt],dis(u,fa[rt])-val[u]);
    127     for(int i=head[u];i;i=Next[i]){
    128         int v=ver[i];
    129         if(v!=f&&!vis[v]) dfs(v,u,rt);
    130     }
    131 }
    132 void solve(int u,int f){
    133     fa[u]=f,vis[u]=1;
    134     int totsz=size;
    135     dfs(u,0,u);
    136     for(int i=head[u];i;i=Next[i]){
    137         int v=ver[i];
    138         if(!vis[v]){
    139             rt=0,size=sz[v]>sz[u]?totsz-sz[u]:sz[v];
    140             findrt(v,0),to[u].push_back(rt),solve(rt,u);
    141         }
    142     }
    143 }
    144 void recover(int x){
    145     //遍历点分树,清空节点 
    146     ++size,vis[x]=0;
    147     Erholung(tree1[x]),Erholung(tree2[x]);
    148     tree1[x]=tree2[x]=null;
    149     for(int i=0,k=to[x].size();i<k;++i) recover(to[x][i]);
    150     to[x].clear();
    151 }
    152 void rebuild(int x){
    153     //点分树某一子树过大,重构 
    154     size=0,recover(x),rt=0,findrt(x,0);
    155     if(fa[x])
    156     for(int i=0,j=to[fa[x]].size();i<j;++i)
    157     if(to[fa[x]][i]==x) to[fa[x]][i]=rt;
    158     solve(rt,fa[x]);
    159 }
    160 ll ans=0;
    161 int insert(int x){
    162     register int i,ds,res=0;
    163     //求出小于等于val[x]-dis(x,fa[i])的个数,只要在平衡树里找小于val[x]-dis(x,fa[i])+1的就可以了 
    164     for(i=x;fa[i];i=fa[i])
    165     ds=val[x]-dis(x,fa[i])+1,ans+=getrk(tree1[fa[i]],ds)-getrk(tree2[i],ds);
    166     Insert(tree1[x],-val[x]);
    167     //然后维护修改 
    168     for(i=x;fa[i];i=fa[i]){
    169         int dist=dis(fa[i],x)-val[x];
    170         Insert(tree1[fa[i]],dist);
    171         Insert(tree2[i],dist);
    172     }
    173     //考虑是否需要拍扁重建 
    174     for(i=x;fa[i];i=fa[i])
    175     if(tree1[i]->sz>=tree1[fa[i]]->sz*alpha+5) res=fa[i];
    176     return res;
    177 }
    178 int main(){
    179     n=read(),n=read();
    180     register int i,j,b,x;
    181     for(bin[0]=i=1;i<=20;++i) bin[i]=bin[i-1]<<1;
    182     while(bin[tp+1]<=n) ++tp;
    183     son[0]=n+1,rt=0,init();
    184     for(int i=1;i<=n;++i){
    185         fa[i]=f[i][0]=read()^(ans%inf),b=read(),val[i]=read();
    186         dep[i]=dep[f[i][0]]+1,len[i]=len[f[i][0]]+b,vis[i]=1;
    187         if(fa[i]) to[fa[i]].push_back(i),add(f[i][0],i);
    188         for(j=1;bin[j]+1<=dep[i];++j) f[i][j]=f[f[i][j-1]][j-1];
    189         x=insert(i);if(x) rebuild(x);
    190         print(ans);
    191     }
    192     Ot();
    193     return 0;
    194 }
  • 相关阅读:
    Combine 框架,从0到1 —— 4.在 Combine 中使用计时器
    Combine 框架,从0到1 —— 4.在 Combine 中使用通知
    Combine 框架,从0到1 —— 3.使用 Subscriber 控制发布速度
    Combine 框架,从0到1 —— 2.通过 ConnectablePublisher 控制何时发布
    使用 Swift Package Manager 集成依赖库
    iOS 高效灵活地配置可复用视图组件的主题
    构建个人博客网站(基于Python Flask)
    Swift dynamic关键字
    Swift @objcMembers
    仅用递归函数操作逆序一个栈(Swift 4)
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9489346.html
Copyright © 2011-2022 走看看