题解 P3920【[WC2014]紫荆花之恋】
题意
给 $n$ 次操作,每次操作加入一个节点 $k$,给出 $k$ 的父亲、$k$ 与父亲之间的边权、$k$ 的点权 $r_k$,操作后求出 $|\{(i,j)|1\le i<j\le k\text{ and }r_i+r_j\ge\text{dis}(i,j)\}|$。强制在线。
$n\le 10^5$。
思路
首先思考一个弱化问题:给一棵树,求出答案。
我们可以思考点分治,设当前分治中心为 $u$。对于在不同子树中的 $i$ 和 $j$,$\text{dis}(i,j)=\text{dis}(i,u)+\text{dis}(j,u)$,则答案转为统计 $r_i+r_j\ge \text{dis}(i,u)+\text{dis}(u,j)$ 的对数。
我们将式子移项得到 $r_i-\text{dis}(i,u)\ge\text{dis}(u,j)-r_j$。我们可以使用一个数据结构,支持插入和求排名操作,可以直接使用平衡树,也可以离散化使用权值线段树、权值树状数组维护 $u$ 所有的 $\text{dis}(u,j)-r_j$,查询 $r_i-\text{dis}(i,u)$ 的排名即可。
考虑强制在线后怎么做,显然我们有点分树,可以动态维护点分治。
但是问题来了:我们动态加点,怎么维护点分树的形态呢?
考虑点分树的复杂度是怎么来的:点分树每个节点的父节点是上层分治取的重心,保证了点分树树高为 $O(\log n)$,使得操作复杂度为 $O(\log n)\times O(ds)$。我们可以首先向直接使用原树作为点分树的方向思考,树高 $O(n)$,显然不行,只能得到 $30\text{pts}$。考虑维护点分树上的子树大小,使用替罪羊树的思想,设定平衡因子 $\alpha$,当 $siz_i>siz_{f_i}\times \alpha$ 时暴力重构这颗点分树子树,此处重构指对这部分子树进行点分治建出点分树。
对于这个算法数据结构的选用提一句:由于强制在线的缘故,无法离散化使用树状数组、线段树,如果使用平衡树的话:重构中重新插入 $O(\log n)$,树高 $O(\log n)$,会重构 $O(\log n)$ 次,时间复杂度 $O(n\log^3n)$,再加上平衡树的大常数可能需要卡常。
我们可以使用一种定期重构的数据结构:由于只需要清空、插入、求排名,可以使用两个 vector $a$ 和 $b$,每次暴力在 $b$ 中插入,当 $b$ 的大小超过预订的阀值 $S$ 时,$O(n)$ 将 $b$ 合并进 $a$ 中,查询排名直接在两个 vector 中 upper_bound 即可。
算一下时间复杂度,询问 $O(n\log n)\times O(\log n)=O(n\log^2n)$,插入 $O(n\log^2nS+\dfrac{n^2\log^2n}{S})$,$S$ 取 $O(\sqrt n)$ 得复杂度 $O(n\sqrt n\log^2 n)$。看似复杂度变差了,但由于 vector 自带的超小常数,反而跑得挺快,完全不需要卡常。
不知道为什么 $\alpha$ 取 $0.95$ 跑得最快。
最后贴一下代码吧:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=1e5+10,S=350,INF=1e7+10;
const double alpha=0.95;
//like the Scapegoat_Tree to rebuild
int head[N],sz[N],cnt,n,r[N];
bool vis[N];
struct Edge{
int to,nxt,val;
}a[N<<1];
inline void add(int u,int v){
cnt++;
a[cnt].to=v;
a[cnt].nxt=head[u];
head[u]=cnt;
}
inline pair<int,int> findroot(int rt,int f,int pre){
sz[rt]=1;
int mx=0;
pair<int,int> ans=make_pair(INF,0);
for(int i=head[rt];i;i=a[i].nxt){
int t=a[i].to;
if(vis[t]||t==f) continue;
ans=min(ans,findroot(t,rt,pre));
sz[rt]+=sz[t];
mx=max(mx,sz[t]);
}
ans=min(ans,make_pair(max(mx,pre-sz[rt]),rt));
return ans;
}
struct Dis{
int f[17][N],dep[N],des[N];
inline void insert(int x,int fa,int c){
f[0][x]=fa;
dep[x]=dep[fa]+1;
des[x]=des[fa]+c;
for(int i=1;i<=16;i++){
f[i][x]=f[i-1][f[i-1][x]];
}
}
inline int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=16;~i;i--){
if(dep[f[i][x]]>=dep[y]){
x=f[i][x];
}
}
if(x==y) return x;
for(int i=16;~i;i--){
if(f[i][x]!=f[i][y]){
x=f[i][x],y=f[i][y];
}
}
return f[0][x];
}
inline int dis(int x,int y){
return des[x]+des[y]-2*des[LCA(x,y)];
}
}bz;
struct Data_Structer{
vector<int>a,b;
inline void insert(int x){
b.insert(lower_bound(b.begin(),b.end(),x),x);
if(b.size()>=S){
vector<int>c,d;
//use 3 not 1 or UB in O2
int i=0,j=0;
for(;i<a.size()&&j<b.size();){
if(a[i]<b[j]){
c.push_back(a[i++]);
}else{
c.push_back(b[j++]);
}
}
while(i<a.size()){
c.push_back(a[i++]);
}
while(j<b.size()){
c.push_back(b[j++]);
}
swap(a,c);
swap(b,d);
}
}
inline int rank(int x){
return (upper_bound(a.begin(),a.end(),x)-a.begin())+(upper_bound(b.begin(),b.end(),x)-b.begin())-2;
}
inline void clear(){
a.clear(),b.clear();
}
}pa[N<<1],pb[N<<1];
//smile
//pa:when i is root pb:subtree f_i->i
struct Point_Divide_Tree{
int f[N],dep[N],siz[N];
//point x value v in subtree Rt
//insert -r into DS count dis(j,u)-r_j≤r_i-dis(i,u)
inline int query(int x,int r){
int ans=0;
for(int i=x,j;f[i];i=j){
j=f[i];
int dis=r-bz.dis(x,j);
ans+=pa[j].rank(dis)-pb[i].rank(dis);
}
return ans;
}
inline void insert(int x,int v,int Rt){
pa[x].insert(-v);
for(int i=x,j;i!=Rt;i=j){
j=f[i];
int dis=bz.dis(x,j)-v;
if(j!=Rt) pa[j].insert(dis);
pb[i].insert(dis);
}
}
//clear "vis" thats dep[i]>deps(rebuild Rt)
inline void clear(int x,int fa,int deps){
vis[x]=0;
for(int i=head[x];i;i=a[i].nxt){
int t=a[i].to;
if(t!=fa&&dep[t]>deps){
clear(t,x,deps);
}
}
}
//build subtree Rt
inline int build(int x,int pre,int fa,int Rt){
int rt=findroot(x,0,pre).second,bas=sz[x];
pa[rt].clear();
pb[rt].clear();
f[rt]=fa;
siz[rt]=1;
vis[rt]=1;
dep[rt]=dep[fa]+1;
insert(rt,r[rt],Rt);
for(int i=head[rt];i;i=a[i].nxt){
int t=a[i].to;
if(!vis[t]){
siz[rt]+=build(t,bas,rt,Rt);
}
}
return siz[rt];
}
//rebuild subtree x
inline void rebuild(int x){
if(!x) return ;
clear(x,0,dep[x]);
build(x,siz[x],f[x],f[x]);
}
inline void insert(int x,int v){
pa[x].insert(-v);
siz[x]++;
int Rt=0;
for(int i=x,j;f[i];i=j){
j=f[i];
int dis=bz.dis(x,j)-v;
siz[j]++;
pa[j].insert(dis);
pb[i].insert(dis);
if(siz[i]>siz[j]*alpha){
Rt=j;
}
}
rebuild(Rt);
}
}t;
int main(){
ll last=0;
read();
n=read();
for(int i=1;i<=n;i++){
vis[i]=1;
int fa=read()^last%1000000000,c=read();
r[i]=read();
bz.insert(i,fa,c);
if(i>1){
add(fa,i);
add(i,fa);
}
t.f[i]=fa;
t.dep[i]=t.dep[fa]+1;
write(last+=t.query(i,r[i]));
t.insert(i,r[i]);
putc('\n');
}
flush();
}
/*
Time:52.31s
Memory:162.77MB
*/
再见 qwq~

浙公网安备 33010602011771号