cunzai_zsy0531

关注我

P3920 [WC2014]紫荆花之恋 题解

题面

这题真的非常 nb!

如果我们不管这个加叶子的操作,只考虑统计答案:每一次统计和点 \(i\) 能够成对的个数。这个可以用点分树做。注意到那个条件 \(dist(i,j)\leq r_i+r_j\) 相当于 \(dist(i,l)-r_i\leq r_j-dist(j,l)\),每个点统计到这个点的 \(dis-r\),拿 \(r_i-dis(i,j)\)\(j\) 点去找排名即可,还有点分树的那个容斥。这个可以使用平衡树,也可以根号重构。根号重构的做法是,考虑每个点维护两个 vector\(big,small\),一开始把数都放到 \(small\) 里边,如果大小 \(>300\) 就全部倒进 \(big\) 里边并且排好序,这个过程可以使用归并排序。然后查一个数排名就在 \(big\)upper_bound,在 \(small\) 里暴力统计即可。复杂度是一个根号。

现在考虑如何处理这个动态的树。一个很自然的想法是类似替罪羊重构,如果 \(siz_u>\alpha\times siz_{fa_u}\),每次找到最浅的一个 \(u\),重构 \(fa_u\) 整个子树。这个过程的复杂度也不是很高,大概 \(2log\)\(3log\)。实测取 \(\alpha=0.8\) 可以通过。

这道题在写的过程中出现了较多问题:

  1. 注意判断重构之前,要按照不重构的方式,把新加的点的所有数组都搞一遍。一开始的第一个点不要忘了特殊处理。

  2. 点分树的结构特殊,很容易想当然。点分树的一个子树在原树上是且仅是一个联通块,它四面八方都可能和其他连通块(在点分树上不相干的点)相邻,所以需要重构的话,就把每个子树都有哪些点记录下来就行了。

点击查看代码
inline void clearvector(std::vector<ll> &b){std::vector<ll> c;std::swap(b,c);}
inline void clearvector2(std::vector<int> &b){std::vector<int> c;std::swap(b,c);}
const int N=1e5+13,Mod=1e9;
const double alpha=0.8021;
struct Edge{int v,w,nxt;}e[N<<1];
int n,h[N],etot;
ll a[N];
inline void add_edge(int u,int v,int w){e[++etot]=(Edge){v,w,h[u]};h[u]=etot;}

namespace Tree{
const int logN=21;
int fa[N][logN],dep[N];ll dis[N];
inline int lca(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=20;i>=0;--i)
		if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
	if(u==v) return u;
	for(int i=20;i>=0;--i)
		if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
inline ll dist(int u,int v){return dis[u]+dis[v]-2*dis[lca(u,v)];}
}

struct Node{
	std::vector<ll> small,big;
	inline void clear(){clearvector(small),clearvector(big);}
	inline void insert(ll x){small.pb(x);}
	inline int calc(ll x){
		if(small.size()>300){
			int lims=small.size(),limb=big.size();
			std::vector<ll> tmp;tmp.resize(lims+limb);
			std::sort(small.begin(),small.end());
			int i=0,j=0,k=0;
			while(i<lims||j<limb){
				if(i>=lims) tmp[k]=big[j++];
				else if(j>=limb) tmp[k]=small[i++];
				else if(small[i]<=big[j]) tmp[k]=small[i++];
				else tmp[k]=big[j++];
				++k;
			}
			std::swap(big,tmp),clearvector(small);
		}
		int res=std::upper_bound(big.begin(),big.end(),x)-big.begin();
		for(auto y:small) res+=(y<=x);
		return res;
	}
}ch[N],d[N];
int fa[N],siz[N],rt,maxx[N],psum;
bool vis[N];
std::vector<int> son[N];
void findrt(int u,int f){
	siz[u]=1,maxx[u]=0;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==f||vis[v]) continue;
		findrt(v,u);
		siz[u]+=siz[v];
		maxx[u]=max(maxx[u],siz[v]);
	}
	maxx[u]=max(maxx[u],psum-siz[u]);
	if(maxx[u]<maxx[rt]) rt=u;
}
void dfs(int u,int f,ll dis,Node &g1,Node &g2,std::vector<int> &g3){
	g1.insert(dis-a[u]),g2.insert(dis-a[u]),g3.pb(u);
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(v!=f&&!vis[v]) dfs(v,u,dis+e[i].w,g1,g2,g3);
	}
}
void build(int u){
	vis[u]=1;
	son[u].clear();son[u].pb(u);
	ch[u].clear();ch[u].insert(-a[u]);
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(vis[v]) continue;
		rt=0,psum=siz[v];
		findrt(v,0),findrt(rt,0);
		fa[rt]=u;d[rt].clear();
		dfs(v,0,e[i].w,ch[u],d[rt],son[u]);
		build(rt);
	}
}
int main(){
#ifdef LOCAL
	freopen("P3920_3.in","r",stdin);
	freopen("data.out","w",stdout);
#endif
	int testcase;read(testcase);
	read(n),read(a[1]),read(a[1]),read(a[1]);ch[1].insert(-a[1]);
	ll lastans=0;println(lastans);Tree::dep[1]=1;siz[1]=vis[1]=1;son[1].pb(1);
	for(int i=2;i<=n;++i){
		int x,w;read(x),read(w),read(a[i]);x^=(lastans%Mod);
		add_edge(x,i,w),add_edge(i,x,w);
		Tree::fa[i][0]=fa[i]=x;Tree::dep[i]=Tree::dep[x]+1;Tree::dis[i]=Tree::dis[x]+w;
		for(int p=i;p;p=fa[p]) siz[p]++,son[p].pb(i);
		for(int j=1;j<=20;++j) Tree::fa[i][j]=Tree::fa[Tree::fa[i][j-1]][j-1];
		ll ans=lastans;
		for(int p=i;fa[p];p=fa[p]){
			ll dd=Tree::dist(i,fa[p]);
			ans+=ch[fa[p]].calc(a[i]-dd)-d[p].calc(a[i]-dd);
			ch[fa[p]].insert(dd-a[i]),d[p].insert(dd-a[i]);
		}
		ch[i].insert(-a[i]),vis[i]=1;
		int p=0;
		for(int now=i;fa[now];now=fa[now])
			if(siz[now]>alpha*siz[fa[now]]) p=fa[now];
		if(p){
			for(auto x:son[p]) vis[x]=0;
			Node ttmp=d[p];int tttmp=fa[p];
			maxx[rt=0]=INF,psum=siz[p];
			findrt(p,0),findrt(rt,0);
			d[rt]=ttmp,fa[rt]=tttmp;
			build(rt);
		}
//		lastans=ans;
		println(lastans=ans);
//		printf("%d\n",i);
	}
	return 0;
}
posted @ 2022-05-11 19:01  cunzai_zsy0531  阅读(46)  评论(0编辑  收藏  举报