[WC2014]紫荆花之恋 解题报告 (动态点分治)

[WC2014]紫荆花之恋 解题报告 (动态点分治)

[WC2014]紫荆花之恋

题意

一棵带权树, 每个点有一个属性 \(r_i\),

两个节点 \(i,j\) 成为朋友当且仅当 \(dis(i,j) \le r_i + r_j\).

树上原来没有节点, 每向树上加入一个节点后, 求树上总共有多少对朋友, 强制在线.

思路

考虑树的形态固定时应该怎么做.

考虑点分治.

对于每个重心 \(u\) , 设 \(i,j\) 在它的管理范围内, 且在两棵不同的子树内,

\(dis(i,j) \le r_i + r_j\) 可变为 \(dis(i,u) + dis(j,u) \le r_i + r_j\),

即求满足 \(dis(j,u)-r_j \le r_i-dis(i,u)\) 的点对个数.

按照点分治的套路, 设 \(ft[u]\) 为点 \(u\) 在点分树上的父亲, 对每个点维护两个 \(vector\)

\(v1[u]\) : \(u\) 的管理范围内的节点, 按照到 \(u\) 的距离 (从小到大) 排序.

\(v2[u]\) : \(u\) 的管理范围内的节点, 按照到 \(ft[u]\) 的距离 (从小到大) 排序.

然后直接点分治就行了.

现在, 树的形态是不固定的.

如果没有强制在线的话, 可以先把树建出来后建好点分树, 按照一般的动态点分治来写就好了.


动态点分治不能处理 树形态发生改变 的情况, 是因为树的形态改变会导致重心的改变, 那么点分树 树高为 \(\log n\) 的优美性质就不存在了, 从而会导致复杂度 (时间和空间) 的退化.


那为了防止复杂度的退化, 我们可以 动态维护重心, 也就是当原来的重心和现在的重心偏离到一定规模时, 重新做一遍点分治, 重构点分树.


那么, 我们考虑一下, 为了进行以上的操作, 需要维护什么东西.

首先, 由于树上的点是一个一个插入的, 而 \(vector\) 不支持插入操作, 所以 \(vector\) 必须被换掉, 换成一个支持排序, 插入的数据结构 (删除的话其实不是刚需, 因为重构点分树时直接清空就好了).

这里我们选择用 平衡树. ( \(set\) 本质上就是一个平衡树 (红黑树), 并且常数比较大, 所以这里我们不用它).

光有平衡树还不够, 因为当重构点分树时, 我们不可能每次都对点分树进行重构, 这样时间复杂度会达到 \(O(n^2 \log n)\), 所以我们每次只能重构点分树的一个子树, 那么我们就还需要记录 每个点在点分树中的子孙, 这个的话用个 \(vector\) 就行了.


现在还剩下一个问题, 如何判断什么时候需要重构点分树?

我们可以对每个点记录一个 \(sz\), 表示它的点分树的子树大小,

再定义一个 $\alpha \in (0.5,1) $, 当存在点 \(u\) 满足 \(sz[u] > \alpha \times sz[ft[u]]\) 时, 就重构 \(ft[u]\) 的子树.

那么每次我们找到最高的满足上述条件的点, 然后重构它的子树就好了, \(\alpha\) 的话取 \([0.75,0.85]\) 左右比较好.


至于平衡树, 一开始我是写的 \(treap\), 但是太慢了, \(T\) 4个点 (听说指针板的 \(treap\) 更快, 但我不会),

后来换成了替罪羊树就过了.

代码

#include<bits/stdc++.h>
#define ll long long
#define db double
#define pb push_back
#define sz size
#define rfs(x) sz[(x)]=sz[ls[(x)]]+sz[rs[(x)]]+num[(x)]
using namespace std;
const int _=1e5+7;
const int __=1e7+7;
const int L=17; 
const int inf=0x3f3f3f3f;
const ll mod=1e9;
bool be;
int ty,n,f[_][L+7],dep[_],dis[_],ft[_],q[_],r[_];   // 点分治, lca
ll ans;
int rt1[_],rt2[_],ls[__],rs[__],val[__],sz[__],num[__],stk[__],cnt;  // 1: to self   2: to father   平衡树
int ddis[_],ssz[_],rt,minx;   // 点分治
int lst[_],nxt[2*_],to[2*_],len[2*_],tot;   // 建边
bool exi[_],vis[_];    // 点分治
vector<int> vec[_];
db ar=0.76,ra=0.76;
bool en;
int gi(){
  char c=getchar(); int x=0;
  while(c<'0'||c>'9') c=getchar();
  while(c>='0'&&c<='9'){ x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
  return x;
}
void pu(ll x){
  if(!x){ putchar('0'); return; }
  ll y=1;
  while(y<=x) y=(y<<3)+(y<<1);
  while(y>1){ y/=10; putchar(x/y+'0'); x%=y; }
}
void add(int x,int y,int w){ nxt[++tot]=lst[x]; to[tot]=y; len[tot]=w; lst[x]=tot; }
int Lca(int x,int y){
  if(dep[x]<dep[y]) swap(x,y);
  for(int i=L;i>=0;i--)
    if(dep[f[x][i]]>=dep[y])
      x=f[x][i];
  if(x==y) return x;
  for(int i=L;i>=0;i--)
    if(f[x][i]!=f[y][i]){
      x=f[x][i];
      y=f[y][i];
    }
  return f[x][0];
}
int dist(int x,int y){ return dis[x]+dis[y]-2*dis[Lca(x,y)]; }
int seq[__],p;
void get(int u){
  if(ls[u]) get(ls[u]);
  seq[++p]=u;
  if(rs[u]) get(rs[u]);
}
void arrange(int &u,int l,int r){
  if(l>r){ u=0; return; }
  int mid=(l+r)>>1;
  u=seq[mid];
  arrange(ls[u],l,mid-1);
  arrange(rs[u],mid+1,r);
  sz[u]=sz[ls[u]]+sz[rs[u]]+num[u];
}
void rebuild(int &u){
  p=0;
  get(u);
  arrange(u,1,p);
}
void insert(int &u,int w,bool fl){
  if(u){
    sz[u]++;
    bool flag=0;
    if(val[u]==w){ num[u]++; exi[u]=1; }
    else if(val[u]>w){
      if(!fl&&(db)(sz[ls[u]]+1)>sz[u]*ra) flag=1;
      insert(ls[u],w,fl|flag);
    }
    else{
      if(!fl&&(db)(sz[rs[u]]+1)>sz[u]*ra) flag=1;
      insert(rs[u],w,fl|flag);
    }
    if(flag) rebuild(u);
  }
  else{
    u=stk[cnt--];
    val[u]=w;
    sz[u]=num[u]=1;
    ls[u]=rs[u]=0;
  }
}
void clear(int &u){
  if(ls[u]) clear(ls[u]);
  if(rs[u]) clear(rs[u]);
  stk[++cnt]=u;
  u=0;
}
int query(int u,int w){
  int res=0;
  while(u){
    if(val[u]==w){ res+=sz[ls[u]]+num[u]; break; }
    else if(val[u]>w) u=ls[u];
    else{ res+=sz[ls[u]]+num[u]; u=rs[u]; }
  }
  return res;
}
int run(int x,int &t){
  int len,u=x,fa=ft[u],res=0;
  insert(rt1[x],-r[x],0);
  vec[x].pb(x);
  while(fa){
    len=dist(fa,x);
    res+=query(rt1[fa],r[x]-len)-query(rt2[u],r[x]-len);
    insert(rt1[fa],len-r[x],0);
    insert(rt2[u],len-r[x],0);
    vec[fa].pb(x);
    if((db)vec[u].sz()>vec[fa].sz()*ar) t=fa;
    u=ft[u]; fa=ft[u];
  }
  return res;
}
int que[_],t;
void g_rt(int u,int fa,int sum){
  //printf("   u: %d\n",u);
  int maxn=0; que[++t]=u;
  ssz[u]=1;
  for(int i=lst[u];i;i=nxt[i]){
    int v=to[i];
    if(exi[v]&&!vis[v]&&v!=fa){
      ddis[v]=ddis[u]+len[i];
      g_rt(v,u,sum);
      ssz[u]+=ssz[v];
      maxn=max(maxn,ssz[v]);
    }
  }
  maxn=max(maxn,sum-ssz[u]);
  if(maxn<minx){ minx=maxn; rt=u; }
}
void count(int u,int fa){
  que[++t]=u;
  for(int i=lst[u];i;i=nxt[i])
    if(!vis[to[i]]&&exi[to[i]]&&to[i]!=fa){
      ddis[to[i]]=ddis[u]+len[i];
      count(to[i],u);
    }
}
void calc(int u){
  t=0;
  for(int i=lst[u];i;i=nxt[i])
    if(!vis[to[i]]&&exi[to[i]]){
      ddis[to[i]]=len[i];
      count(to[i],0);
    }
  for(int i=1;i<=t;i++){
    insert(rt1[u],ddis[que[i]]-r[que[i]],0);    // 区分 que 和 q
    vec[u].pb(que[i]);
  }
  insert(rt1[u],-r[u],0);    // 记得把自己加上去
  vec[u].pb(u);
}
void pdiv(int u,int lrt,int sum){
  minx=inf; t=0;
  g_rt(u,0,ssz[u]<ssz[lrt] ?ssz[u] :sum-ssz[lrt]);
  if(lrt){
    if(ddis[u]>=0){
      for(int i=1;i<=t;i++){
    insert(rt2[rt],ddis[que[i]]-r[que[i]],0);
      }
    }
    else{
      for(int i=1;i<=t;i++){
    insert(rt2[rt],dist(que[i],lrt)-r[que[i]],0);
      }
    }
  }
  sum=ssz[u]; ft[rt]=lrt; vis[rt]=1; u=rt;
  for(int i=lst[u];i;i=nxt[i]){
    //printf("%d\n",u);
    if(!vis[to[i]]&&exi[to[i]]){
      ddis[to[i]]=len[i];
      pdiv(to[i],u,sum);
    }
  }
  calc(u);
  vis[u]=0;
}
void adj(int u,int x){
  if(!x) return;
  int top=0;
  for(int i=0;i<(int)vec[x].sz();i++) q[++top]=vec[x][i];
  for(int i=1;i<=top;i++){                           // 清空
    exi[q[i]]=1;
    if(rt1[q[i]]) clear(rt1[q[i]]);
    if(rt2[q[i]]) clear(rt2[q[i]]);
    vector<int>().swap(vec[q[i]]);
    
  }
  ddis[x]=-1;
  ssz[ft[x]]=0;
  pdiv(x,ft[x],top);
  //puts("!!!");
  for(int i=1;i<=top;i++) ssz[q[i]]=ddis[q[i]]=exi[q[i]]=0;
}
int main(){
  //freopen("x.in","r",stdin);
  //freopen("x.out","w",stdout);
  cin>>ty>>n; int c,x; ll a;
  clock_t st=clock();
  for(int i=1;i<=__-7;i++) stk[i]=i; cnt=__-7;
  for(int i=1;i<=n;i++){
    a=(ll)gi(); c=gi(); r[i]=gi();
    x=0;
    a^=ans%mod;
    f[i][0]=ft[i]=(int)a;
    for(int k=1;k<=L;k++)
      f[i][k]=f[f[i][k-1]][k-1];
    dep[i]=dep[ft[i]]+1;
    dis[i]=dis[ft[i]]+c;
    add(i,ft[i],c);
    add(ft[i],i,c);
    ans+=run(i,x);
    pu(ans); putchar('\n');
    adj(i,x);
  }
  //printf("\nused time: %.2lfs\n",(db)(clock()-st)/CLOCKS_PER_SEC);
  //printf("\nused space: %.2lfMB\n",(&en-&be)/(1<<20)*1.0);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/brucew-07/p/12144854.html