• P4074 [WC2013]糖果公园


    P4074 [WC2013]糖果公园

    Tag:树上带修莫队

    题意:树上每个点有一种糖果,求(sum_csum_{i=1}^{cnt_c}v_c*w_i)

    其中c为糖果种类,(cnt_c)其为出现次数。

    思路

    离线树上带修莫队。

    先进行树上分块。分块内的询问按照出发点、终止点、询问id优先级依次递减排序。

    对于树上莫队,其实就是在欧拉序上莫队。因为欧拉序的性质,即每个节点子树内的节点一定会经过两次,我们就可以用一个括号序列的方式在莫队时消除子树内无用节点的影响。

    具体来说,序列长度为2*n,每一个节点出入队时我们异或它是否在队中即可。也就是记录每个点出队和入队的时间戳,然后在序列上修改。

    注意,对于LCA其实在欧拉序时是没有包括的,所以我们需要单独求一下LCA的影响。但是如果一个端点本身就是LCA就不用啦。

    但是!用指针实现也太low了,我们直接利用它的树形结构莫队就行(实际上就是我看错了题解)

    代码

    分块和排序

    	int dfs(int x,int f){//毒瘤的树分块而非序列分块
    		int siz=0;
    		fa[x][0]=f;dep[x]=dep[f]+1;
    		dfn[x]=++tim;
    		for(int i=0;i<=20;i++)fa[x][i+1]=fa[fa[x][i]][i];
    		for(int i=head[x];i;i=nxt[i]){
    			int u=to[i];
    			if(u==f)continue;
    			siz+=dfs(u,x);
    			if(siz>=_nsiz){
    				_n++;
    				while(siz)belong[st[top--]]=_n,siz--;
    			}
    		}
    		st[++top]=x;
    		return siz+1;
    	}
    //----------------
    	++_n;
    	while(top)belong[st[top--]]=_n;
    
    inline bool operator < (const query&zp)const {return belong[u]<belong[zp.u] or (belong[u]==belong[zp.u] and (belong[v]<belong[zp.v] or (belong[v]==belong[zp.v] and id<zp.id)));}//运算符版
    

    莫队

    	inline void reverse(const int& x,ll &ans){//将进队的出队,出队的进队。
    		if(vis[x])ans-=1ll*w[num[color[x]]]*v[color[x]],num[color[x]]--;
    		else num[color[x]]++,ans+=1ll*w[num[color[x]]]*v[color[x]];
    		vis[x]^=1;
    	}
    	inline void solve(int x,int y,ll &ans){//直接利用树形结构跳father,更新答案,反正括号序列也就是翻转。
    		while(x!=y){
    			if(dep[x]>dep[y])reverse(x,ans),x=fa[x][0];
    			else reverse(y,ans),y=fa[y][0];
    		}
    	}
    
    		for(int i=1;i<=cntq;i++){
    			while(now<cntm and mo[now+1].id<=q[i].id){//处理时间问题,只能暴力消除影响。
    				now++;
    				if(vis[mo[now].pos])ans-=w[num[color[mo[now].pos]]]*v[color[mo[now].pos]],num[color[mo[now].pos]]--;
    				color[mo[now].pos]=mo[now].aft;
    				if(vis[mo[now].pos])num[color[mo[now].pos]]++,ans+=1LL*w[num[color[mo[now].pos]]]*v[color[mo[now].pos]];
    			}
    			while(now>=1 and mo[now].id>=q[i].id){
    				if(vis[mo[now].pos])ans-=w[num[color[mo[now].pos]]]*v[color[mo[now].pos]],num[color[mo[now].pos]]--;
    				color[mo[now].pos]=mo[now].bef;//注意变成了什么(我就是傻
    				if(vis[mo[now].pos])num[color[mo[now].pos]]++,ans+=1LL*w[num[color[mo[now].pos]]]*v[color[mo[now].pos]];
    				now--;
    			}
    			if(i==1)solve(q[i].u,q[i].v,ans);
    			else solve(q[i].u,q[i-1].u,ans),solve(q[i].v,q[i-1].v,ans);//保证继承答案连续
    			int lca=LCA(q[i].u,q[i].v);
    			reverse(lca,ans);
    			q[i].ans=ans;
    			reverse(lca,ans);//单独计算,最后要消除影响。不要被继承。
    		}
    

    事实证明,这种写法是真毒瘤,开了O2才勉强能过。

    全代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<cmath>
    using namespace std;
    inline int read(){
    	int w=0,x=0;char c=getchar();
    	while(!isdigit(c))w|=c=='-',c=getchar();
    	while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    	return w?-x:x;
    }
    namespace star
    {
    	const int maxn=1e5+10;
    	typedef long long ll;
    	int n,m,Q,_n,fa[maxn][23],_nsiz,dfn[maxn],dep[maxn],c[maxn],color[maxn];
    	ll v[maxn],w[maxn];
    	int ecnt,tim,st[maxn<<1],belong[maxn],top,head[maxn],to[maxn<<1],nxt[maxn<<1];
    	struct query{
    		int u,v,id;
    		ll ans;
    		inline bool operator < (const query&zp)const {return belong[u]<belong[zp.u] or (belong[u]==belong[zp.u] and (belong[v]<belong[zp.v] or (belong[v]==belong[zp.v] and id<zp.id)));}
    	}q[maxn];
    	inline bool cmp(query a,query b){return a.id<b.id;}
    	struct modify{
    		int pos,bef,aft,id;
    	}mo[maxn<<1];
    	inline void addedge(int a,int b){
    		to[++ecnt]=b,nxt[ecnt]=head[a],head[a]=ecnt;
    		to[++ecnt]=a,nxt[ecnt]=head[b],head[b]=ecnt;
    	}
    	int dfs(int x,int f){
    		int siz=0;
    		fa[x][0]=f;dep[x]=dep[f]+1;
    		dfn[x]=++tim;
    		for(int i=0;i<=20;i++)fa[x][i+1]=fa[fa[x][i]][i];
    		for(int i=head[x];i;i=nxt[i]){
    			int u=to[i];
    			if(u==f)continue;
    			siz+=dfs(u,x);
    			if(siz>=_nsiz){
    				_n++;
    				while(siz)belong[st[top--]]=_n,siz--;
    			}
    		}
    		st[++top]=x;
    		return siz+1;
    	}
    	inline int LCA(int x,int y){
    		if(dep[x]<dep[y])swap(x,y);
    		for(int i=20;i+1;i--)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
    		if(x==y)return x;
    		for(int i=20;i+1;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    		return fa[x][0];
    	}
    	bool vis[maxn];
    	int num[maxn];
    	inline void reverse(const int& x,ll &ans){
    		if(vis[x])ans-=1ll*w[num[color[x]]]*v[color[x]],num[color[x]]--;
    		else num[color[x]]++,ans+=1ll*w[num[color[x]]]*v[color[x]];
    		vis[x]^=1;
    	}
    	inline void solve(int x,int y,ll &ans){
    		while(x!=y){
    			if(dep[x]>dep[y])reverse(x,ans),x=fa[x][0];
    			else reverse(y,ans),y=fa[y][0];
    		}
    	}
    	inline void work(){
    		n=read(),m=read(),Q=read();
    		_nsiz=pow(n,0.666666666);
    		for(int i=1;i<=m;i++)v[i]=read();
    		for(int i=1;i<=n;i++)w[i]=read();
    		for(int i=1;i<n;i++)addedge(read(),read());
    		for(int i=1;i<=n;i++)c[i]=color[i]=read();
    		int cntq=0,cntm=0,x;
    		for(int i=1;i<=Q;i++){
    			if(read())q[++cntq].id=i,q[cntq].u=read(),q[cntq].v=read();
    			else mo[++cntm].id=i,mo[cntm].bef=c[(x=read())],mo[cntm].pos=x,mo[cntm].aft=c[x]=read();
    		}
    		dfs(1,0);
    		for(int i=1;i<=cntq;i++)if(dfn[q[i].v]<dfn[q[i].u])swap(q[i].u,q[i].v);//对于这种写法这句其实可有也可无了。这种写法的一个好处就是不交换不影响正确性。
    		++_n;
    		while(top)belong[st[top--]]=_n;
    		sort(q+1,q+1+cntq);
    		int now=0;
    		long long ans=0;
    		for(int i=1;i<=cntq;i++){
    			while(now<cntm and mo[now+1].id<=q[i].id){
    				now++;
    				if(vis[mo[now].pos])ans-=w[num[color[mo[now].pos]]]*v[color[mo[now].pos]],num[color[mo[now].pos]]--;
    				color[mo[now].pos]=mo[now].aft;
    				if(vis[mo[now].pos])num[color[mo[now].pos]]++,ans+=1LL*w[num[color[mo[now].pos]]]*v[color[mo[now].pos]];
    			}
    			while(now>=1 and mo[now].id>=q[i].id){
    				if(vis[mo[now].pos])ans-=w[num[color[mo[now].pos]]]*v[color[mo[now].pos]],num[color[mo[now].pos]]--;
    				color[mo[now].pos]=mo[now].bef;
    				if(vis[mo[now].pos])num[color[mo[now].pos]]++,ans+=1LL*w[num[color[mo[now].pos]]]*v[color[mo[now].pos]];
    				now--;
    			}
    			if(i==1)solve(q[i].u,q[i].v,ans);
    			else solve(q[i].u,q[i-1].u,ans),solve(q[i].v,q[i-1].v,ans);
    			int lca=LCA(q[i].u,q[i].v);
    			reverse(lca,ans);
    			q[i].ans=ans;
    			reverse(lca,ans);
    		}
    		sort(q+1,q+1+cntq,cmp);
    		for(int i=1;i<=cntq;i++)printf("%lld
    ",q[i].ans);
    	}
    }
    signed main(){
    	star::work();
    	return 0;
    }
    

    总结

    写了个假的(gxy称之为非主流)树上莫队。再写一道免得脑子了装着奇怪的东西。

    因为写法毒瘤所以#define int long long会T飞。

    大家千万不要学非主流

  • 相关阅读:
    最全的静态网站生成器(开源项目)
    移动互联网流量变现模式调研问卷
    公众平台商户接入(微信支付)功能申请教程
    微信支付全面开放
    百度天气预报接口
    微信公众平台开发(83) 生成带参数二维码
    微信支付接口申请指南
    微信自媒体账号涉违规大规模被封
    php大文件上传解决方案支持分片断点上传
    html5大文件上传解决方案(500M以上)
  • 原文地址:https://www.cnblogs.com/BrotherHood/p/13709188.html
Copyright © 2020-2023  润新知