zoukankan      html  css  js  c++  java
  • [UOJ#576][ULR#1]服务器调度

    XXVIII.[UOJ#576][ULR#1]服务器调度

    非常可怕的大数据结构题,原版代码整整码了9K,就算稍微合并合并也剩下7K……

    首先,我们考虑对每种颜色,建出一棵虚树。考虑求出虚树的一条直径。则有个结论是原树上到任意一点最远的点肯定是此直径的端点之一。

    例如,我们考虑下方的这棵树:

    		O			
    	       / \				
    	      Z	  V			
    	     / \				
    	    M   Y
               / \
              X   U
    

    设我们求出虚树的直径是 X,Y。我们考虑求出该直径的中点是 M。则显然,M 一定位于 XY 中深度更大的那一个节点到二者 LCA 的路径上。例如,在上图中,就可以看到图上的 M 在深度更大的 X 一侧。我们在接下来的讲解中,都会默认 X 是更深的那一个。同时,可以找到它们的 LCAZ

    则,MM 子树中所有节点,关于此种颜色的最远点都是 Y;而 M 子树外所有节点的最远点都是 X


    我们先考虑子树中的一个点,上述的 U。明显,此种颜色对其贡献,是 \(dis_U+dis_Y-2dis_{\text{lca}(U,Y)}\)

    我们发现,必有 \(\text{lca}(U,Y)=Z\)。故上式可以被转换成 \(dis_U+dis_Y-2dis_Z\)

    此式可以被拆作两部分——\(dis_U\),此部分我们接下来再说。\(dis_Y-2dis_Z\),其对于 M 子树中所有点(即所有可能的 U)都是常数,故我们可以直接上一棵线段树对其进行子树加来维护此部分。


    我们再考虑子树外的一个点,上述的 V。则此种颜色对其贡献是 \(dis_V+dis_X-2dis_{\text{lca}(V,X)}\)

    此式可以被拆作三部分。第一部分,\(dis_V\)。结合上前面的 \(dis_U\),我们发现其范围就是整棵树。则,每种颜色都会对整棵树覆盖一次,一共有 \(m\) 种颜色,则一共会覆盖 \(m\) 次。在询问时,我们直接上一个前缀和维护子树中所有东西的 \(dis\) 之和,再乘上一个 \(m\) 即为此部分答案。

    第二部分,\(dis_X\)。其仍是常数,仍可使用我们上述的子树加线段树处理。

    第三部分,\(-2dis_{\text{lca}(V,X)}\)。其可被先转换成 \(-2dis_{\text{lca}(V,M)}\),但仍然不好处理。

    我们考虑对 M 到根的路径上所有边都打上一个 \(-2\)tag(使用树剖)。这样,点 V 到根的路径上所有边的长度的带权(权为 tag)和就是 \(dis_{\text{lca}(V,M)}\)。但是,一个问题是,此方案不仅会影响 M 子树外的点,更会影响 M 子树中的点。

    但是,我们发现,此方案对 M 子树中所有点的影响都是固定的——都是 \(-2dis_M\)。故我们直接仍在上述子树加线段树上把这部分影响给它补回去即可,即执行子树加 \(2dis_M\)


    我们再来考虑求答案时的操作。子树加线段树上的操作以及前缀和之类的都可以简单完成。关键还是上述 V 的第三部分操作比较烦人——它要求出子树中所有点到根路径的带权距离和。设当前询问的点是 Q

    考虑每条边被覆盖多少次。明显,Q 到根路径上所有边,覆盖其的路径数量都为 Q 的子树大小。故我们只需求出 Q 到根路径的带权长度,再乘上 Q 的子树大小,即为此部分贡献。到根路径的带权长度,仍可使用树剖解决。

    而考虑 Q 子树中的任意一条边 X-Y,其中 XY 的父亲,其被覆盖了 Y 子树大小次。故我们仍可以求带权子树和,此处权仍然是边上的 tag,但值是 \(\text{边的长度}\times\text{Y子树大小}\)。则其可再开一棵线段树解决。


    于是我们便解决了这个问题。剩下的就是我们之前忽略的一些问题了。例如,对于每种颜色,如何维护其直径?

    众所周知的是,有一个结论,两组点集合并时,新点集的直径的端点一定来自于两点集中原本的直径端点(不管其是来自于同一点集还是不同点集)。这就意味着我们可以使用线段树维护不同颜色的端点了(当然,用的是动态开点线段树。实际上直接开 \(m\) 棵平衡树维护也不失为一个好选择,但是那样子码量和常数都会噌噌上涨)。


    最后是具体实现时的一些问题。例如,虽然总复杂度是 \(O(n\log^2n)\) 的(来自于树剖),但是事实上是常数极大(毕竟我们前面开了好几棵线段树),需要榨取所有可以被优化的常数。所以,求直径时用 \(O(1)\)RMQ-LCA 罢。而同时,为了实现求一条直径的中点,我们还要再预处理倍增数组以方便树上二分。于是你将会发现本题需要全部三种LCA算法

    同时,因为各种东西全掺在一块会使得条理很不清晰,大剂量的 namespace 等封装在 debug 时是很有必要的(同时,也方便把已经 debug 完的部分缩起来避免看偏)

    但是,就算这样说,最后还免不得TLE的悲惨命运。以下是TLE的初版代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n;
    const int N=200100;
    int col[N];
    int dep[N],dis[N],fa[N],dfn[N],rev[N],top[N],sz[N],num[N];
    namespace TRE{//works about the original tree
    	int head[N],cnt;
    	struct node{int to,next,val;}edge[N];
    	void ae(int u,int v,int w){edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;}
    	namespace SPR{//tree-chain separation.
    		int tot,son[N];
    		void dfs1(int x){
    			sz[x]=1;
    			for(int i=head[x],y;i!=-1;i=edge[i].next){
    				dep[y=edge[i].to]=dep[x]+1,dis[y]=dis[x]+edge[i].val,fa[y]=x,dfs1(y),sz[x]+=sz[y];
    				if(sz[y]>=sz[son[x]])son[x]=y;
    			}
    		}
    		void dfs2(int x){
    			dfn[x]=++tot,rev[tot]=x;
    			if(!top[x])top[x]=x;
    			if(son[x])top[son[x]]=top[x],dfs2(son[x]);
    			for(int i=head[x];i!=-1;i=edge[i].next)if(edge[i].to!=son[x])dfs2(edge[i].to);
    		}
    	}
    	namespace ST{//ST table for LCA part
    		int MN[N<<1][20],LG[N<<1],FIR[N],lim;
    		void dfs(int x){
    			MN[++lim][0]=x,FIR[x]=lim;
    			for(int i=head[x];i!=-1;i=edge[i].next)dfs(edge[i].to),MN[++lim][0]=x;
    		}
    		int MIN(int x,int y){return dep[x]<dep[y]?x:y;}
    		void init(){
    			for(int i=2;i<=lim;i++)LG[i]=LG[i>>1]+1;
    			for(int j=1;j<=LG[lim];j++)for(int i=1;i+(1<<j)-1<=lim;i++)MN[i][j]=MIN(MN[i][j-1],MN[i+(1<<(j-1))][j-1]);
    		}
    		int LCA(int x,int y){
    			x=FIR[x],y=FIR[y];
    			if(x>y)swap(x,y);
    			int k=LG[y-x+1];
    			return MIN(MN[x][k],MN[y-(1<<k)+1][k]);
    		}
    		int DIS(int x,int y){return dis[x]+dis[y]-2*dis[LCA(x,y)];}
    	}
    	using ST::LCA;
    	namespace DOB{//doubling on tree part for ancestor tracing
    		int anc[N<<1][20];
    		void init(){
    			for(int i=1;i<=n;i++)anc[i][0]=fa[i];
    			for(int j=1;j<=19;j++)for(int i=1;i<=n;i++)anc[i][j]=anc[anc[i][j-1]][j-1];
    		}
    		int MID(int &x,int &y){//using doubling on tree to calculate the midpoint of a route
    			int z=LCA(x,y);
    			if(dis[x]<=dis[y])swap(x,y);
    			int r=x;
    			for(int i=19;i>=0;i--)if(dep[r]-(1<<i)>=dep[z]&&dis[x]-dis[anc[r][i]]<=dis[y]+dis[anc[r][i]]-2*dis[z])r=anc[r][i];
    			return r;
    		}
    	}
    	void init(){SPR::dfs1(1),SPR::dfs2(1),ST::dfs(1),ST::init(),DOB::init();}
    }
    using TRE::LCA;
    using TRE::DOB::MID;
    using TRE::ST::DIS;
    namespace DIA{//calculating the diameter of the tree
    	int rt[N],cnt,bin[N*20],tp;
    	struct SegTree{//a dynamic-nodeadding segtree, calculating diameters of each colour's virtual trees 
    		int lson,rson,u,v;
    	}seg[N*20];
    	#define mid ((l+r)>>1)
    	void pushup(int &x){
    		seg[x].u=seg[x].v=0;
    		if(!seg[x].lson&&!seg[x].rson){bin[++tp]=x,x=0;return;}
    		if(!seg[x].rson){seg[x].u=seg[seg[x].lson].u,seg[x].v=seg[seg[x].lson].v;return;}
    		if(!seg[x].lson){seg[x].u=seg[seg[x].rson].u,seg[x].v=seg[seg[x].rson].v;return;}
    		int a[4]={seg[seg[x].lson].u,seg[seg[x].lson].v,seg[seg[x].rson].u,seg[seg[x].rson].v};
    		for(int i=0;i<4;i++)for(int j=i+1;j<4;j++)if((!seg[x].u&&!seg[x].v)||DIS(seg[x].u,seg[x].v)<DIS(a[i],a[j]))seg[x].u=a[i],seg[x].v=a[j];
    	}
    	int NewNode(){return tp?bin[tp--]:++cnt;}
    	void Insert(int &x,int l,int r,int P){
    //		printf("%d %d:%d\n",l,r,P);
    		if(l>P||r<P)return;
    		if(!x)x=NewNode();
    		if(l==r){seg[x].u=seg[x].v=rev[P];return;}
    		Insert(seg[x].lson,l,mid,P),Insert(seg[x].rson,mid+1,r,P),pushup(x);
    	}
    	void Delete(int &x,int l,int r,int P){
    		if(l>P||r<P)return;
    		if(l==r){seg[x].u=seg[x].v=0,bin[++tp]=x,x=0;return;}
    		Delete(seg[x].lson,l,mid,P),Delete(seg[x].rson,mid+1,r,P),pushup(x);
    	}
    //	void Iterate(int x,int l,int r){
    //		if(!x)return;
    //		printf("%d:[%d,%d]:(%d,%d)\n",x,l,r,seg[x].u,seg[x].v);
    //		Iterate(seg[x].lson,l,mid),Iterate(seg[x].rson,mid+1,r);
    //	}
    	#undef mid
    }
    ll sumdis[N];
    namespace SUB{
    	#define lson x<<1
    	#define rson x<<1|1
    	#define mid ((l+r)>>1)
    	struct SegTree{ll sum,tag;}seg[N<<2];
    	void ADD(int x,int l,int r,ll y){seg[x].sum+=y*(r-l+1),seg[x].tag+=y;}
    	void pushup(int x){seg[x].sum=seg[lson].sum+seg[rson].sum;}
    	void pushdown(int x,int l,int r){ADD(lson,l,mid,seg[x].tag),ADD(rson,mid+1,r,seg[x].tag),seg[x].tag=0;}
    	void modify(int x,int l,int r,int L,int R,int val){
    		if(l>R||r<L)return;
    		if(L<=l&&r<=R){ADD(x,l,r,val);return;}
    		pushdown(x,l,r),modify(lson,l,mid,L,R,val),modify(rson,mid+1,r,L,R,val),pushup(x);
    	}
    	ll query(int x,int l,int r,int L,int R){
    		if(l>R||r<L)return 0;
    		if(L<=l&&r<=R)return seg[x].sum;
    		pushdown(x,l,r);return query(lson,l,mid,L,R)+query(rson,mid+1,r,L,R);
    	}
    	#undef lson
    	#undef rson
    	#undef mid
    }
    namespace ALT{//segtree of alter-subtree jobs(using SPR things)
    	namespace SBT{//segment tree for subtree works
    		#define lson x<<1
    		#define rson x<<1|1
    		#define mid ((l+r)>>1)
    		struct SegTree{ll sumsz,tag,sum;}seg[N<<2];
    		void ADD(int x,ll y){seg[x].tag+=y,seg[x].sum+=y*seg[x].sumsz;}
    		void pushdown(int x){ADD(lson,seg[x].tag),ADD(rson,seg[x].tag),seg[x].tag=0;}
    		void pushup(int x){seg[x].sum=seg[lson].sum+seg[rson].sum;}
    		void build(int x,int l,int r){
    			if(l==r){seg[x].sumsz=1ll*sz[rev[l]]*num[rev[l]];return;}
    			build(lson,l,mid),build(rson,mid+1,r),seg[x].sumsz=seg[lson].sumsz+seg[rson].sumsz;
    		}
    		void modify(int x,int l,int r,int L,int R,ll val){
    			if(l>R||r<L)return;
    			if(L<=l&&r<=R){ADD(x,val);return;}
    			pushdown(x),modify(lson,l,mid,L,R,val),modify(rson,mid+1,r,L,R,val),pushup(x);
    		}
    		ll query(int x,int l,int r,int L,int R){
    			if(l>R||r<L)return 0;
    			if(L<=l&&r<=R)return seg[x].sum;
    			pushdown(x);return query(lson,l,mid,L,R)+query(rson,mid+1,r,L,R);
    		}
    		#undef lson
    		#undef rson
    		#undef mid			
    	}
    	namespace CHT{//segtree of chain jobs
    		#define lson x<<1
    		#define rson x<<1|1
    		#define mid ((l+r)>>1)
    		struct SegTree{ll sumnum,tag,sum;}seg[N<<2];
    		void ADD(int x,ll y){seg[x].tag+=y,seg[x].sum+=y*seg[x].sumnum;}
    		void pushdown(int x){ADD(lson,seg[x].tag),ADD(rson,seg[x].tag),seg[x].tag=0;}
    		void pushup(int x){seg[x].sum=seg[lson].sum+seg[rson].sum;}
    		void build(int x,int l,int r){
    			if(l==r){seg[x].sumnum=num[rev[l]];return;}
    			build(lson,l,mid),build(rson,mid+1,r),seg[x].sumnum=seg[lson].sumnum+seg[rson].sumnum;
    		}
    		void modify(int x,int l,int r,int L,int R,ll val){
    			if(l>R||r<L)return;
    			if(L<=l&&r<=R){ADD(x,val);return;}
    			pushdown(x),modify(lson,l,mid,L,R,val),modify(rson,mid+1,r,L,R,val),pushup(x);
    		}
    		ll query(int x,int l,int r,int L,int R){
    			if(l>R||r<L)return 0;
    			if(L<=l&&r<=R)return seg[x].sum;
    			pushdown(x);return query(lson,l,mid,L,R)+query(rson,mid+1,r,L,R);
    		}
    		#undef lson
    		#undef rson
    		#undef mid	
    	}
    	void modify(int l,int r,int val){SBT::modify(1,1,n,l,r,val),CHT::modify(1,1,n,l,r,val);}
    	void chainmod(int x,int val){while(x)modify(dfn[top[x]],dfn[x],val),x=fa[top[x]];}
    	ll chainsum(int x){ll ret=0;while(x)ret+=CHT::query(1,1,n,dfn[top[x]],dfn[x]),x=fa[top[x]];return ret;}
    }
    void diameter(int x,int y,int dir){//works need to do when have a diameter of (x-y).
    	int m=MID(x,y);
    	int z=LCA(x,y);
    //	printf("%d %d %d %d\n",x,y,m,z);
    //	printf("%d %d %d %d\n",x,y,m,z);
    	SUB::modify(1,1,n,dfn[m],dfn[m]+sz[m]-1,dir*(dis[y]-2*dis[z]+2*dis[m]));
    //	printf("SUB%d:%d\n",m,dis[y]-2*dis[z]);
    //	printf("ALTER%d:%d\n",m,dis[x]);
    //	printf("CHAIN%d:%d\n",m,-2);
    	SUB::modify(1,1,n,1,dfn[m]-1,dir*dis[x]);
    	SUB::modify(1,1,n,dfn[m]+sz[m],n,dir*dis[x]);
    	ALT::chainmod(m,-2*dir);
    }
    int m,q;
    ll query(int x){
    	ll res=0;
    	res+=(sumdis[dfn[x]+sz[x]-1]-sumdis[dfn[x]-1])*m;
    	res+=SUB::query(1,1,n,dfn[x],dfn[x]+sz[x]-1);
    //	printf("CHAS:%d\n",ALT::chainsum(fa[x]));
    //	printf("SBT:%d\n",ALT::SBT::query(1,1,n,dfn[x],dfn[x]+sz[x]-1));
    	res+=ALT::chainsum(fa[x])*sz[x];
    	res+=ALT::SBT::query(1,1,n,dfn[x],dfn[x]+sz[x]-1);
    	return res;
    }
    #define dia(i) DIA::seg[DIA::rt[i]].u,DIA::seg[DIA::rt[i]].v
    int main(){
    	scanf("%d%d%d",&n,&m,&q),memset(TRE::head,-1,sizeof(TRE::head));
    	for(int i=1;i<=n;i++)scanf("%d",&col[i]);
    	for(int i=2;i<=n;i++)scanf("%d",&fa[i]);
    	for(int i=2;i<=n;i++)scanf("%d",&num[i]),TRE::ae(fa[i],i,num[i]);
    	TRE::init();
    	ALT::SBT::build(1,1,n);
    	ALT::CHT::build(1,1,n);
    //	for(int i=1;i<=n;i++)printf("%d:FA:%d SZ:%d SN:%d DF:%d RV:%d TP:%d DP:%d\n",i,fa[i],sz[i],TRE::SPR::son[i],dfn[i],rev[i],top[i],dep[i]);
    	for(int i=1;i<=n;i++)sumdis[i]=sumdis[i-1]+dis[rev[i]];
    	for(int i=1;i<=n;i++)DIA::Insert(DIA::rt[col[i]],1,n,dfn[i]);
    	
    //	diameter(dia(4),1);
    //	m=1;
    //	printf("%d\n",query(4));
    //	m=4;
    //	diameter(dia(4),-1);
    	
    	for(int i=1;i<=m;i++)diameter(dia(i),1);
    	
    	for(int t,x;q;q--){
    		scanf("%d%d",&t,&x);
    		if(t==1){
    			diameter(dia(col[x]),-1),DIA::Delete(DIA::rt[col[x]],1,n,dfn[x]),diameter(dia(col[x]),1);
    			scanf("%d",&col[x]);
    			diameter(dia(col[x]),-1),DIA::Insert(DIA::rt[col[x]],1,n,dfn[x]),diameter(dia(col[x]),1);
    		}else printf("%lld\n",query(x));
    	}
    	return 0;
    }
    

    最终,我用树状数组替换掉了其中三棵线段树(将其模板化)。最终便卡过去了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n;
    const int N=200100;
    int col[N];
    int dep[N],dis[N],fa[N],dfn[N],rev[N],top[N],sz[N],num[N];
    namespace TRE{//works about the original tree
    	int head[N],cnt;
    	struct node{int to,next,val;}edge[N];
    	void ae(int u,int v,int w){edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;}
    	namespace SPR{//tree-chain separation.
    		int tot,son[N];
    		void dfs1(int x){
    			sz[x]=1;
    			for(int i=head[x],y;i!=-1;i=edge[i].next){
    				dep[y=edge[i].to]=dep[x]+1,dis[y]=dis[x]+edge[i].val,fa[y]=x,dfs1(y),sz[x]+=sz[y];
    				if(sz[y]>=sz[son[x]])son[x]=y;
    			}
    		}
    		void dfs2(int x){
    			dfn[x]=++tot,rev[tot]=x;
    			if(!top[x])top[x]=x;
    			if(son[x])top[son[x]]=top[x],dfs2(son[x]);
    			for(int i=head[x];i!=-1;i=edge[i].next)if(edge[i].to!=son[x])dfs2(edge[i].to);
    		}
    	}
    	namespace ST{//ST table for LCA part
    		int MN[N<<1][20],LG[N<<1],FIR[N],lim;
    		void dfs(int x){
    			MN[++lim][0]=x,FIR[x]=lim;
    			for(int i=head[x];i!=-1;i=edge[i].next)dfs(edge[i].to),MN[++lim][0]=x;
    		}
    		int MIN(int x,int y){return dep[x]<dep[y]?x:y;}
    		void init(){
    			for(int i=2;i<=lim;i++)LG[i]=LG[i>>1]+1;
    			for(int j=1;j<=LG[lim];j++)for(int i=1;i+(1<<j)-1<=lim;i++)MN[i][j]=MIN(MN[i][j-1],MN[i+(1<<(j-1))][j-1]);
    		}
    		int LCA(int x,int y){
    			x=FIR[x],y=FIR[y];
    			if(x>y)swap(x,y);
    			int k=LG[y-x+1];
    			return MIN(MN[x][k],MN[y-(1<<k)+1][k]);
    		}
    		int DIS(int x,int y){return dis[x]+dis[y]-2*dis[LCA(x,y)];}
    	}
    	using ST::LCA;
    	namespace DOB{//doubling on tree part for ancestor tracing
    		int anc[N<<1][20];
    		void init(){
    			for(int i=1;i<=n;i++)anc[i][0]=fa[i];
    			for(int j=1;j<=19;j++)for(int i=1;i<=n;i++)anc[i][j]=anc[anc[i][j-1]][j-1];
    		}
    		int MID(int &x,int &y){//using doubling on tree to calculate the midpoint of a route
    			int z=LCA(x,y);
    			if(dis[x]<=dis[y])swap(x,y);
    			int r=x;
    			for(int i=19;i>=0;i--)if(dep[r]-(1<<i)>=dep[z]&&dis[x]-dis[anc[r][i]]<=dis[y]+dis[anc[r][i]]-2*dis[z])r=anc[r][i];
    			return r;
    		}
    	}
    	void init(){SPR::dfs1(1),SPR::dfs2(1),ST::dfs(1),ST::init(),DOB::init();}
    }
    using TRE::LCA;
    using TRE::DOB::MID;
    using TRE::ST::DIS;
    namespace DIA{//calculating the diameter of the tree
    	int rt[N],cnt,bin[N*20],tp;
    	struct SegTree{//a dynamic-nodeadding segtree, calculating diameters of each colour's virtual trees 
    		int lson,rson,u,v;
    	}seg[N*20];
    	#define mid ((l+r)>>1)
    	void pushup(int &x){
    		seg[x].u=seg[x].v=0;
    		if(!seg[x].lson&&!seg[x].rson){bin[++tp]=x,x=0;return;}
    		if(!seg[x].rson){seg[x].u=seg[seg[x].lson].u,seg[x].v=seg[seg[x].lson].v;return;}
    		if(!seg[x].lson){seg[x].u=seg[seg[x].rson].u,seg[x].v=seg[seg[x].rson].v;return;}
    		int a[4]={seg[seg[x].lson].u,seg[seg[x].lson].v,seg[seg[x].rson].u,seg[seg[x].rson].v};
    		for(int i=0;i<4;i++)for(int j=i+1;j<4;j++)if((!seg[x].u&&!seg[x].v)||DIS(seg[x].u,seg[x].v)<DIS(a[i],a[j]))seg[x].u=a[i],seg[x].v=a[j];
    	}
    	int NewNode(){return tp?bin[tp--]:++cnt;}
    	void Insert(int &x,int l,int r,int P){
    //		printf("%d %d:%d\n",l,r,P);
    		if(l>P||r<P)return;
    		if(!x)x=NewNode();
    		if(l==r){seg[x].u=seg[x].v=rev[P];return;}
    		Insert(seg[x].lson,l,mid,P),Insert(seg[x].rson,mid+1,r,P),pushup(x);
    	}
    	void Delete(int &x,int l,int r,int P){
    		if(l>P||r<P)return;
    		if(l==r){seg[x].u=seg[x].v=0,bin[++tp]=x,x=0;return;}
    		Delete(seg[x].lson,l,mid,P),Delete(seg[x].rson,mid+1,r,P),pushup(x);
    	}
    //	void Iterate(int x,int l,int r){
    //		if(!x)return;
    //		printf("%d:[%d,%d]:(%d,%d)\n",x,l,r,seg[x].u,seg[x].v);
    //		Iterate(seg[x].lson,l,mid),Iterate(seg[x].rson,mid+1,r);
    //	}
    	#undef mid
    }
    struct BIT{//a universal template of fenwick tree
    	ll s[N];//the prefix sum of original array
    	ll t1[N],t2[N];//the unweighted and weighted fenwick
    	ll&operator[](const int &x){return s[x];}
    	BIT(){memset(s,0,sizeof(s)),memset(t1,0,sizeof(t1)),memset(t2,0,sizeof(t2));}
    	void ADD(int x,int y){for(int i=x;i<=n;i+=i&-i)t1[i]+=y,t2[i]+=1ll*s[x-1]*y;}
    	ll SUM(int x){
    		ll sum=0;
    		for(int i=x;i;i-=i&-i)sum+=t1[i];
    		sum*=s[x];
    		for(int i=x;i;i-=i&-i)sum-=t2[i];
    		return sum;
    	}
    	void ADD(int l,int r,int val){if(l<=r)ADD(l,val),ADD(r+1,-val);}
    	ll SUM(int l,int r){return SUM(r)-SUM(l-1);}
    }SUB,SBT,CHT;
    ll sumdis[N];
    namespace ALT{//segtree of alter-subtree jobs(using SPR things)
    	void modify(int l,int r,int val){SBT.ADD(l,r,val),CHT.ADD(l,r,val);}
    	void chainmod(int x,int val){while(x)modify(dfn[top[x]],dfn[x],val),x=fa[top[x]];}
    	ll chainsum(int x){ll ret=0;while(x)ret+=CHT.SUM(dfn[top[x]],dfn[x]),x=fa[top[x]];return ret;}
    }
    void diameter(int x,int y,int dir){//works need to do when have a diameter of (x-y).
    	int m=MID(x,y);
    	int z=LCA(x,y);
    	SUB.ADD(dfn[m],dfn[m]+sz[m]-1,dir*(dis[y]-2*dis[z]+2*dis[m]));
    	SUB.ADD(1,dfn[m]-1,dir*dis[x]);
    	SUB.ADD(dfn[m]+sz[m],n,dir*dis[x]);
    	ALT::chainmod(m,-2*dir);
    }
    int m,q;
    ll query(int x){
    	ll res=0;
    	res+=(sumdis[dfn[x]+sz[x]-1]-sumdis[dfn[x]-1])*m;
    	res+=SUB.SUM(dfn[x],dfn[x]+sz[x]-1);
    //	printf("CHAS:%d\n",ALT::chainsum(fa[x]));
    //	printf("SBT:%d\n",ALT::SBT::query(1,1,n,dfn[x],dfn[x]+sz[x]-1));
    	res+=ALT::chainsum(fa[x])*sz[x];
    	res+=SBT.SUM(dfn[x],dfn[x]+sz[x]-1);
    	return res;
    }
    #define dia(i) DIA::seg[DIA::rt[i]].u,DIA::seg[DIA::rt[i]].v
    int main(){
    	scanf("%d%d%d",&n,&m,&q),memset(TRE::head,-1,sizeof(TRE::head));
    	for(int i=1;i<=n;i++)scanf("%d",&col[i]);
    	for(int i=2;i<=n;i++)scanf("%d",&fa[i]);
    	for(int i=2;i<=n;i++)scanf("%d",&num[i]),TRE::ae(fa[i],i,num[i]);
    	TRE::init();
    	for(int i=1;i<=n;i++)SUB[i]=i;
    	for(int i=1;i<=n;i++)SBT[i]=SBT[i-1]+1ll*sz[rev[i]]*num[rev[i]];
    	for(int i=1;i<=n;i++)CHT[i]=CHT[i-1]+num[rev[i]];
    //	for(int i=1;i<=n;i++)printf("%d:FA:%d SZ:%d SN:%d DF:%d RV:%d TP:%d DP:%d\n",i,fa[i],sz[i],TRE::SPR::son[i],dfn[i],rev[i],top[i],dep[i]);
    	for(int i=1;i<=n;i++)sumdis[i]=sumdis[i-1]+dis[rev[i]];
    	for(int i=1;i<=n;i++)DIA::Insert(DIA::rt[col[i]],1,n,dfn[i]);
    	
    //	diameter(dia(4),1);
    //	m=1;
    //	printf("%d\n",query(4));
    //	m=4;
    //	diameter(dia(4),-1);
    	
    	for(int i=1;i<=m;i++)diameter(dia(i),1);
    	
    	for(int t,x;q;q--){
    		scanf("%d%d",&t,&x);
    		if(t==1){
    			diameter(dia(col[x]),-1),DIA::Delete(DIA::rt[col[x]],1,n,dfn[x]),diameter(dia(col[x]),1);
    			scanf("%d",&col[x]);
    			diameter(dia(col[x]),-1),DIA::Insert(DIA::rt[col[x]],1,n,dfn[x]),diameter(dia(col[x]),1);
    		}else printf("%lld\n",query(x));
    	}
    	return 0;
    }
    

  • 相关阅读:
    [InnoSetup]Inno Setup软件打包脚本
    inno setup 执行SQL
    用inno Setup制作web项目安装包
    Inno Setup执行SQL脚本的方法
    delphi的tserversocket控件如何接收16进制数
    Delphi 通信报Asynchronous socket error 10053错误的一个解决方法
    么正矩阵(酉矩阵)
    对称矩阵、Hermite矩阵、正交矩阵、酉矩阵、奇异矩阵、正规矩阵、幂等矩阵
    RSVP协议的基本概念介绍
    计算机顶级会议的历年最佳文章
  • 原文地址:https://www.cnblogs.com/Troverld/p/14611536.html
Copyright © 2011-2022 走看看