zoukankan      html  css  js  c++  java
  • [WC2013] 糖果公园

    III.[WC2013] 糖果公园

    树上莫队。

    树上莫队是依照于一种特殊的序列:括号序列而进行的莫队。括号序列,具体而言,是 dfs 树的时候,到一个点就插入序列,离开一个点的时候再插入一次。这样,所有在树上 \(x\sim y\) 路径上出现的点,在括号序列上就是自 \(x\) 第一次出现的位置到 \(y\) 第一次出现的位置,这一段上所有出现过恰一次的值——出现过零次的值显然不在考虑范围之内,而出现过两次的值是进了子树又出来的。可以开一个 vis 数组来表示每个点出现了奇数次还是偶数次。

    但需要注意的是,若 LCA 并非两端点之一,它就不会被考虑到路径上,因此需要特判;若 \(x\) 不是 LCA,它也不会被考虑,也需要特判。

    然后剩下就是带修莫队板子了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int BBB=2200;
    int n,m,q,a[100100],b[100100],c[100100],buc[100100],st[200100][20],fir[100100],LG[200100],tot,dep[100100],bra[200100],lef[200100],cnt,lim;
    bool vis[100100];
    ll res,ans[100100];
    vector<int>v[100100];
    void dfs(int x,int fa){
    	st[++tot][0]=x,fir[x]=tot,dep[x]=dep[fa]+1,bra[++cnt]=x,lef[x]=cnt;
    	for(auto y:v[x])if(y!=fa)dfs(y,x),st[++tot][0]=x;
    	bra[++cnt]=x;
    }
    int MIN(int x,int y){return dep[x]<dep[y]?x:y;}
    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(st[x][k],st[y-(1<<k)+1][k]);
    }
    struct modify{int x,bef,aft;}r[100100];
    struct query{
    	int x,y,z,id;
    	friend bool operator<(const query&u,const query&v){
    		if(lef[u.x]/BBB!=lef[v.x]/BBB)return lef[u.x]<lef[v.x];
    		if(lef[u.y]/BBB!=lef[v.y]/BBB)return lef[u.y]<lef[v.y];
    		return u.z<v.z;
    	}
    }p[100100];
    void Rev(int x){if(vis[x])res-=1ll*b[buc[c[x]]--]*a[c[x]];else res+=1ll*b[++buc[c[x]]]*a[c[x]];vis[x]^=1;}
    int L,R,T;
    void Tplus(){++T;if(vis[r[T].x])Rev(r[T].x),c[r[T].x]=r[T].aft,Rev(r[T].x);else c[r[T].x]=r[T].aft;}
    void Tminus(){if(vis[r[T].x])Rev(r[T].x),c[r[T].x]=r[T].bef,Rev(r[T].x);else c[r[T].x]=r[T].bef;T--;}
    int main(){
    	scanf("%d%d%d",&n,&m,&q);
    	for(int i=1;i<=m;i++)scanf("%d",&a[i]);
    	for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    	for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
    	dfs(1,0);
    	for(int i=2;i<=tot;i++)LG[i]=LG[i>>1]+1;
    	for(int j=1;j<=LG[tot];j++)for(int i=1;i+(1<<j)-1<=tot;i++)st[i][j]=MIN(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    //	for(int i=1;i<=cnt;i++)printf("%d ",bra[i]);puts("");
    //	for(int i=1;i<=n;i++)printf("%d ",lef[i]);puts("");
    	for(int i=1;i<=n;i++)scanf("%d",&c[i]); 
    	for(int i=1,t,x,y;i<=q;i++){
    		scanf("%d%d%d",&t,&x,&y);
    		if(!t)r[++lim].x=x,r[lim].bef=c[x],r[lim].aft=c[x]=y,i--,q--;
    		else{
    			if(lef[x]>lef[y])swap(x,y);
    			p[i].x=x,p[i].y=y,p[i].z=lim,p[i].id=i;
    		}
    	}
    //	for(int i=1;i<=lim;i++)printf("(%d,%d,%d)\n",r[i].x,r[i].bef,r[i].aft);
    //	for(int i=1;i<=q;i++)printf("[%d,%d,%d]\n",p[i].x,p[i].y,p[i].z);
    	L=1,R=0,T=lim;
    	sort(p+1,p+q+1);
    	for(int i=1;i<=q;i++){
    		while(L>lef[p[i].x])Rev(bra[--L]);
    		while(R<lef[p[i].y])Rev(bra[++R]);
    		while(L<lef[p[i].x])Rev(bra[L++]);
    		while(R>lef[p[i].y])Rev(bra[R--]);
    		while(T>p[i].z)Tminus();
    		while(T<p[i].z)Tplus();
    		int lca=LCA(p[i].x,p[i].y);
    		if(lca!=p[i].x&&lca!=p[i].y)Rev(lca);
    		if(p[i].x!=lca)Rev(p[i].x);
    //		printf("A:%d\n",p[i].id);
    		ans[p[i].id]=res;
    		if(p[i].x!=lca)Rev(p[i].x);
    		if(lca!=p[i].x&&lca!=p[i].y)Rev(lca);
    	}
    	for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
    	return 0;
    }
    

  • 相关阅读:
    root登录出现“sorry, that didn't work please try again”
    【自适应辛普森】积分计算
    【CF1553F】Pairwise Modulo
    调和级数的复杂度
    CF 1600-2000 的思维题
    中超热身赛(2021湘潭全国邀请赛-重现)补题
    牛客2021年度训练联盟热身训练赛第一场(讲题)
    新知识-Queue_循环队列
    新知识-valueOf(Leetcode 1556_千位分隔符)
    新知识-位运算(Leetcode 217_存在重复元素)
  • 原文地址:https://www.cnblogs.com/Troverld/p/14620550.html
Copyright © 2011-2022 走看看