zoukankan      html  css  js  c++  java
  • 【BZOJ3435】[Wc2014]紫荆花之恋 替罪点分树+SBT

    【BZOJ3435】[Wc2014]紫荆花之恋

    Description

    强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点。每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 i 都有一个感受能力值Ri ,小精灵 i, j 成为朋友当且仅当在树上 i 和 j 的距离 dist(i,j) ≤ Ri + R! ,其中 dist(i, j)表示在这个树上从 i 到 j 的唯一路径上所有边的边权和。强强和萌萌很好奇每次新长出一个叶子节点之后,这个树上总共有几对朋友。  
    我们假定这个树一开始为空,节点按照加入的顺序从 1开始编号。由于强强非常好奇, 你必须在他每次出现新节点后马上给出总共的朋友对数,不能拖延哦。 

    Input

    共有 n + 2 行。 
    第一行包含一个正整数,表示测试点编号。 
    第二行包含一个正整数 n ,表示总共要加入的节点数。 
    我们令加入节点前的总共朋友对数是 last_ans,在一开始时它的值为0。 
    接下来 n 行中第 i 行有三个数 ai, bi, ri,表示节点  i  的父节点的编号为 ai xor (last_ans mod 10^9)   (其中xor 表示异或,mod  表示取余,数据保证这样操作后得到的结果介于 1到i  –  1之间),与父节点之间的边权为 ci,节点 i 上小精灵的感受能力值为r!。 
    注意 a1 = c1 = 0,表示 1 号点是根节点,对于 i > 1,父节点的编号至少为1。

    Output

    包含 n 行,每行输出1 个整数, 表示加入第 i 个点之后,树上有几对朋友。

    Sample Input

    0
    5
    0 0 6
    1 2 4
    0 9 4
    0 5 5
    0 2 4

    Sample Output

    0
    1
    2
    4
    7

    HINT

    1<=Ci<=10000
    Ai<=2*10^9
    Ri<=10^9
    N<=100000

    题解:在GXZ的蛊惑下,特地去学了一发SBT~

    显然要将题中的式子拆一下:dis(i,j)<=ri+rj  -> dis(i,lca)+dis(j,lca)<=ri+rj -> dis(i,lca)-ri<=rj-dis(j,lca)。

    所以我们对点分树上的每个节点都维护一个SBT记录所有dis(i,x)-ri,然后每次加点时,在点分树上到根的路径上的每个点的SBT都查一下就行了。由于有重复计算,所以对于每个点我们还要维护一个SBT记录所有dis(i,fa[x])-ri,查询时减去即可。

    但是动态加点怎么办?将点分树变成替罪的即可。

    其实代码并不是特别长,如果TLE了一定是写挂了~

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int maxn=100010;
    const ll mod=1000000000;
    int n,m,tot,cnt,mn,root;
    ll ans;
    struct sbt
    {
    	int siz,ch[2],val;
    }t[20000005];
    vector<int> ch[maxn];
    int r[maxn],f[19][maxn],siz[maxn],dep[maxn],head[maxn],next[maxn<<1],to[maxn<<1],vis[maxn],p[maxn];
    int r1[maxn],r2[maxn],fa[maxn],dd[maxn],Log[maxn];
    queue<int> q;
    int mem;
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    void pushup(int x)	{t[x].siz=t[t[x].ch[0]].siz+t[t[x].ch[1]].siz+1;}
    void rotate(int &x,int d)
    {
    	int y=t[x].ch[d];
    	t[x].ch[d]=t[y].ch[d^1],t[y].ch[d^1]=x;
    	pushup(x),pushup(y),x=y;
    }
    void maintain(int &x,int d)
    {
    	if(t[t[t[x].ch[d]].ch[d]].siz>t[t[x].ch[d^1]].siz)
    		rotate(x,d);
    	else	if(t[t[t[x].ch[d]].ch[d^1]].siz>t[t[x].ch[d^1]].siz)
    		rotate(t[x].ch[d],d^1),rotate(x,d);
    	else	return;
    	maintain(t[x].ch[0],0),maintain(t[x].ch[1],1);
    	maintain(x,0),maintain(x,1);
    }
    void insert(int &x,int y)
    {
    	if(!x)
    	{
    		mem--;
    		x=q.front(),q.pop();
    		t[x].siz=1,t[x].val=y,t[x].ch[0]=t[x].ch[1]=0;
    		return ;
    	}
    	int d=(y>=t[x].val);
    	t[x].siz++,insert(t[x].ch[d],y);
    	maintain(x,d);
    }
    int query(int x,int y)
    {
    	if(!x)	return 0;
    	if(t[x].val<=y)	return query(t[x].ch[1],y)+1+t[t[x].ch[0]].siz;
    	return query(t[x].ch[0],y);
    }
    void del(int &x)
    {
    	if(!x)	return ;
    	mem++;
    	del(t[x].ch[0]),del(t[x].ch[1]),t[x].siz=t[x].val=0,q.push(x),x=0;
    }
    void getrt(int x,int fa)
    {
    	siz[x]=1;
    	int i,tmp=0;
    	for(i=head[x];i!=-1;i=next[i])	if(to[i]!=fa&&vis[to[i]]==2)
    		getrt(to[i],x),siz[x]+=siz[to[i]],tmp=max(tmp,siz[to[i]]);
    	tmp=max(tmp,tot-siz[x]);
    	if(tmp<mn)	mn=tmp,root=x;
    }
    void solve(int x)
    {
    	vis[x]=1;
    	for(int i=head[x];i!=-1;i=next[i])	if(vis[to[i]]==2)
    		tot=siz[to[i]],mn=1<<30,getrt(to[i],x),fa[root]=x,solve(root);
    }
    void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    int lca(int a,int b)
    {
    	if(dd[a]<dd[b])	swap(a,b);
    	int i;
    	for(i=Log[dd[a]-dd[b]];i>=0;i--)	if(dd[f[i][a]]>=dd[b])	a=f[i][a];
    	if(a==b)	return b;
    	for(i=Log[dd[a]];i>=0;i--)	if(f[i][a]!=f[i][b])	a=f[i][a],b=f[i][b];
    	return f[0][a];
    }
    int dis(int a,int b)
    {
    	return dep[a]+dep[b]-2*dep[lca(a,b)];
    }
    int main()
    {
    	rd(),n=rd();
    	memset(head,-1,sizeof(head));
    	int i,j,a,b,u,last,flast;
    	mem=20000000;
    	for(i=1;i<=20000000;i++)	q.push(i);
    	rd(),rd(),r[1]=rd(),siz[1]=1,dd[1]=1,ch[1].push_back(1),insert(r1[1],-r[1]);
    	for(i=2;i<=n;i++)	Log[i]=Log[i>>1]+1;
    	printf("0
    ");
    	for(i=2;i<=n;i++)
    	{
    		a=rd()^(ans%mod),b=rd(),r[i]=rd();
    		add(a,i),add(i,a),dep[i]=dep[a]+b,dd[i]=dd[a]+1;
    		f[0][i]=a,fa[i]=a;
    		for(j=1;j<=Log[dd[i]];j++)	f[j][i]=f[j-1][f[j-1][i]];
    		for(last=0,u=i;u;u=fa[u])
    		{
    			ans+=query(r1[u],r[i]-dis(i,u));
    			ch[u].push_back(i),insert(r1[u],dis(i,u)-r[i]),siz[u]++;
    			if(fa[u])
    			{
    				ans-=query(r2[u],r[i]-dis(i,fa[u]));
    				insert(r2[u],dis(i,fa[u])-r[i]);
    			}
    			if(fa[u]&&siz[u]*1.0>(siz[fa[u]]+1)*0.88)	last=fa[u];
    		}
    		if(last)
    		{
    			flast=fa[last],vis[flast]=3;
    			for(p[0]=j=0;j<(int)ch[last].size();j++)	p[++p[0]]=ch[last][j];
    			for(j=1;j<=p[0];j++)	ch[p[j]].clear(),del(r1[p[j]]),del(r2[p[j]]),vis[p[j]]=2;
    			tot=p[0],mn=1<<30,getrt(last,0),fa[root]=flast,solve(root);
    			for(j=1;j<=p[0];j++)
    			{
    				for(u=p[j];u!=flast;u=fa[u])
    				{
    					ch[u].push_back(p[j]);
    					insert(r1[u],dis(p[j],u)-r[p[j]]);
    					if(fa[u])	insert(r2[u],dis(p[j],fa[u])-r[p[j]]);
    				}
    			}
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
  • 相关阅读:
    斐波那契数列
    用两个栈实现队列
    从尾到头打印链表
    HDOJ5877(dfs序+离散化+树状数组)
    HDOJ5876(补图的最短路)
    POJ3090(欧拉函数)
    POJ2478(欧拉函数)
    POJ2407(欧拉函数)
    POJ2142(扩展欧几里得)
    POJ3020(最小边覆盖)
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7452237.html
Copyright © 2011-2022 走看看