BZOJ 3435: [Wc2014]紫荆花之恋 【(替罪羊树式)"动态"点分治 + Treap】

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/88776024

BZOJ 传送门
洛谷传送门

题目分析:

似乎做过几道点分树的题之后题解还是比较好懂的。

这位dalao的题目分析非常的到位,Orz。

PoPoQQQ的具体解读也非常的清晰,Orz。

R i + R j > = d i s t ( i , j ) R i d i s t ( i , k ) > = d i s t ( k , j ) R j R_i+R_j>=dist(i,j) \rarr R_i-dist(i,k)>=dist(k,j)-R_j ,k是i,j路径上的一点。
那么就点分治,每个重心上一棵平衡树tree1保存子树内的点 j j 到它的 d i s t G j R j dist_{Gj}-R_j
儿子上再挂一棵平衡树tree2存儿子对父亲的贡献 d i s t f a   j R j dist_{fa~j}-R_j
插入的时候往上查询<=Ri-dist的数就好了。
由于是动态加点,所以不平衡就重构点分树。
重构一次的时间,由于每个点都是暴力插入平衡树,所以是nlog2n的
期望重构logn次,总复杂度按道理来说应该是nlog3n。不知道为什么很多dalao都说是nlog2n

指针操作的有旋Treap真是上天。。%PoPoQQQ。

然后我就怀抱着激动的心情开始打代码。。
然后就到了半夜两点钟疯狂TLE。。
然后第二天早上起来一看发现是傻逼错误。。(详见代码注释)

敲完紫荆花之恋感觉整个人虚脱至死神清气爽。。

Code:

#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<vector>
#include<algorithm>
#define maxn 100005
#define alpha 0.85
using namespace std;
char cb[1<<15],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
    char c;while(!isdigit(c=getc()));
    for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}

inline int Rnd(){
	static int G = 3;
	return G=G*3ll%998244353;
}

int n,r[maxn];
long long ans;

struct Treap{
	static vector<Treap*>bin;
	Treap *ch[2];
	int val,rnd, cnt,siz;
	void* operator new (size_t,int v){//重载指针操作符真是高级操作。。
		Treap *ret;
		if(!bin.empty()) ret=bin.back(),bin.pop_back();
		else{//这个动态申请内存似乎和fread差不多
			static Treap *mempool=0,*C=0;
			if(C==mempool) mempool=(C=new Treap[1<<15])+(1<<15);
			ret=C++;
		}
		ret->ch[0]=ret->ch[1]=0;//记得清空~
		ret->val=v, ret->rnd=Rnd();
		ret->cnt=ret->siz=1;
		return ret;
	}
	void operator delete (void *p){
		bin.push_back((Treap*)p);
	}
	friend void Delete(Treap *&x){
		if(!x) return;
		Delete(x->ch[0]),Delete(x->ch[1]);
		delete x; x=0;
	}
	
	void upd(){
		siz=cnt;//下面访问空指针会RE
		if(ch[0]) siz+=ch[0]->siz;
		if(ch[1]) siz+=ch[1]->siz;
	}
	friend void rot(Treap *&x,bool c){
		Treap *y=x->ch[c];
		x->ch[c]=y->ch[!c], y->ch[!c]=x;
		x->upd(), y->upd(), x=y;
	}
	friend void insert(Treap *&x,int v){
		if(!x) 	{x=new(v)Treap;return;}
		if(x->val==v) x->cnt++;
		else{
			bool c=v>x->val;
			insert(x->ch[c],v);
			if(x->ch[c]->rnd < x->rnd) rot(x,c);
		}
		x->upd();//插入完要update!!
	}
	friend int query(Treap *x,int v){
		if(!x) return 0;
		if(v<x->val) return query(x->ch[0],v);
		return (x->ch[0]?x->ch[0]->siz:0)+x->cnt+query(x->ch[1],v);
	}
};
vector<Treap*>Treap::bin;//???

int fir[maxn],nxt[maxn<<1],to[maxn<<1],w[maxn<<1],tot;
inline void line(int x,int y,int z){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;w[tot]=z;}
int f[maxn][18],dep[maxn],dis[maxn];
int LCA(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=0,k=dep[u]-dep[v];i<=16;i++) if(k&1<<i) u=f[u][i];
	if(u==v) return u;
	for(int i=16;i>=0;i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
	return f[u][0];
}
inline int Distance(int u,int v){
	return dis[u]+dis[v]-2*dis[LCA(u,v)];
}

namespace Dynamic_TDC{//动态树分治(Divide and Conquer)
	int fa[maxn],idx,vis[maxn];
	Treap *tree1[maxn],*tree2[maxn];
	
	int del(int x,int pre){
		int s=1;
		Delete(tree1[x]);
		for(int i=fir[x];i;i=nxt[i])
			if(vis[to[i]]!=idx&&to[i]!=pre) Delete(tree2[to[i]]),s+=del(to[i],x);
		return s;
	}

	int dfs(int x,int pre,int d,Treap *&p){
		int s=1;
		insert(p,d-r[x]);
		for(int i=fir[x];i;i=nxt[i])
			if(vis[to[i]]!=idx&&to[i]!=pre) s+=dfs(to[i],x,d+w[i],p);
		return s;
	}
	int getroot(int x,int pre,int tsz,int &G){
		int s=1,flg=1;
		for(int i=fir[x];i;i=nxt[i]) if(vis[to[i]]!=idx&&to[i]!=pre){
			int tmp=getroot(to[i],x,tsz,G);
			if((tmp<<1)>tsz) flg=0;
			s+=tmp;
		}
		if(((tsz-s)<<1)>tsz) flg=0;
		if(flg) G=x;
		return s;
	}
	int Tree_Divide_And_Conquer(int x,int tsz){
		getroot(x,0,tsz,x);
		vis[x]=idx;
		dfs(x,0,0,tree1[x]);
		for(int i=fir[x];i;i=nxt[i]) if(vis[to[i]]!=idx){
			Treap *p=0;
			int ss=dfs(to[i],x,w[i],p);
			int tmp=Tree_Divide_And_Conquer(to[i],ss);
			fa[tmp]=x,tree2[tmp]=p;
		}
		return x;
	}

	void rebuild(int x){
		int y=fa[x]; ++idx;
		for(int i=fa[x];i;i=fa[i]) vis[i]=idx;//子树内的节点可能与上面的fa有连边
		
		Treap *p=tree2[x]; tree2[x]=0;//保留tree2
		x=Tree_Divide_And_Conquer(x,del(x,0));
		fa[x]=y, tree2[x]=p;
	}
	void Insert(int x){
		for(int i=x,last=0,dist;i;i=fa[i]){
			if(fa[i]){
				dist=Distance(x,fa[i]);
				ans+=query(tree1[fa[i]],r[x]-dist);
				ans-=query(tree2[i],r[x]-dist);
				insert(tree2[i],dist-r[x]);
			}
			insert(tree1[i],last-r[x]),last=dist;
		}
		//check_rebuild
		int rep = 0;
		for(int i=x;fa[i];i=fa[i])
			if(tree1[fa[i]]->siz * alpha <= tree1[i]->siz) rep=fa[i];
		if(rep) rebuild(rep);
	}
}
int main()
{
	#ifndef ONLINE_JUDGE
		freopen("H.in","r",stdin);
	#endif
	scanf("%*d%d",&n);
	for(int i=1,x,y;i<=n;i++){
		read(x),read(y),read(r[i]),x^=ans%1000000000;
		if(x) line(x,i,y),line(i,x,y);
		f[i][0]=x,dep[i]=dep[x]+1,dis[i]=dis[x]+y;
		for(int j=1;j<=16;j++) f[i][j]=f[f[i][j-1]][j-1];
		
		Dynamic_TDC::fa[i]=x;
		Dynamic_TDC::Insert(i);
		
		printf("%lld\n",ans);
	}
}

猜你喜欢

转载自blog.csdn.net/C20181220_xiang_m_y/article/details/88776024