求调一道题,蒟蒻自闭了
  • 板块学术版
  • 楼主smarthehe
  • 当前回复4
  • 已保存回复4
  • 发布时间2020/5/16 16:25
  • 上次更新2023/11/7 02:21:10
查看原帖
求调一道题,蒟蒻自闭了
103732
smarthehe楼主2020/5/16 16:25

一棵 nn 个点的树,有点权,qq 次操作,每次为以下两种中的一种:

  1. 修改一个点的点权
  2. 设点集 SS,给定 kk 个点,这些点中任意两点为端点的简单路径上的所有点都属于 SS,求 SS 点权和。

n,q105n,q \leq 10^5k106\sum k \leq 10^6

很明显可以用虚树做,每次查询建虚树,点权就是当前点到虚树上的父亲的原树上点权和(不包含父亲)。

那么应该需要维护 sumisum_i 代表 ii 号点到根的距离以方便查询。单点修改对 sumsum 数组的影响可以看作子树修改。于是用 dfs序+树状数组+差分 维护 sumsum 数组。

然而除了样例都过不去,自闭了

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=1e5+5,M=1e6+5;
typedef long long ll;
char buf[1<<20];
int ppp,qqq;
inline char gc()
{
	if(ppp==qqq) ppp=0,qqq=fread(buf,1,1<<20,stdin);
	return buf[ppp++];
}
#define gc() getchar()
inline int rd()
{
	char x;
	while((x=gc())<'0'||x>'9');int t=x-'0';
	while((x=gc())>='0'&&x<='9') t=t*10+x-'0';
	return t;
}
// 以上是读入 
int n,bg[N],nx[N*2],to[N*2],val[N],tl,in[N],out[N],link[N],fa[N][20],dep[N],sum[N],dfn;
ll tr[N];
inline void modify(int pos,int v){for(int i=pos;i<=n;i+=i&-i) tr[i]+=v;}
inline ll query(int pos){ll ret=0;for(int i=pos;i;i-=i&-i) ret+=tr[i];return ret;}
inline void add(int x,int y)
{
	nx[++tl]=bg[x];
	bg[x]=tl;
	to[tl]=y;
}
void dfs(int now,int f,int depth)//预处理 dfs 序,倍增数组,sum 数组 
{
	fa[now][0]=f;dep[now]=depth;
	for(int i=1;i<20;i++)
	{
		if(!fa[fa[now][i-1]][i-1]) break;
		fa[now][i]=fa[fa[now][i-1]][i-1];
	}
	sum[now]=sum[f]+val[now];
	link[++dfn]=now;in[now]=dfn;
	for(int i=bg[now];i;i=nx[i])
	{
		int aim=to[i];
		if(aim==f) continue;
		dfs(aim,now,depth+1);
	}
	out[now]=dfn;
}
int lca(int x,int y)//倍增 lca 
{
	if(dep[x]<dep[y]) swap(x,y);
	int i,delta=dep[x]-dep[y];
	for(i=0;i<20;i++)
	{
		if((1<<i)>delta) break;
		if((1<<i)&delta) x=fa[x][i];
	}
	if(x==y) return x;
	for(i=19;i>0;i--)
	{
		if(fa[x][i]==fa[y][i]) continue;
		x=fa[x][i],y=fa[y][i];
	}
	return fa[x][0];
}
int qry[M],cnt,stk[M],top;
inline int cmp(int x,int y){return in[x]<in[y];}
int main()
{
	n=rd();
	int q=rd(),i,j;
	for(i=1;i<=n;i++) val[i]=rd();
	for(i=1;i<n;i++)
	{
		int x=rd(),y=rd();
		add(x,y),add(y,x);
	}
	dfs(1,0,1);
	for(i=1;i<=n;i++) modify(i,sum[link[i]]-sum[link[i-1]]);//初始化差分树状数组 
	for(i=1;i<=q;i++)
	{
		char typ=gc();
		if(typ=='Q')
		{
			ll ans=0;
			cnt=0;
			int x,tlca;
			while((x=rd())) qry[++cnt]=x;
			sort(qry+1,qry+cnt+1,cmp);
			top=1;stk[1]=1;
			for(j=1;j<=cnt;j++)//虚树 
			{
				if(j==1) tlca=qry[j];
				else tlca=lca(tlca,qry[j]);
				int anc=lca(qry[j],stk[top]);
				if(anc!=stk[top])
				{
					while(dep[anc]<dep[stk[top-1]])
					{
						//printf("%d %d %lld\n",stk[top-1],stk[top],query(in[stk[top]])-query(in[stk[top-1]]));
						ans+=query(in[stk[top]])-query(in[stk[top-1]]);
						top--;
					}
					//printf("%d %d %lld\n",anc,stk[top],query(in[stk[top]])-query(in[anc]));
					ans+=query(in[stk[top--]])-query(in[anc]);
					if(anc!=stk[top]) stk[++top]=anc;
				}
				stk[++top]=qry[j];
			}
			while(top>2)
			{
				//printf("%d %d %lld\n",stk[top-1],stk[top],query(in[stk[top]])-query(in[stk[top-1]]));
				ans+=query(in[stk[top]])-query(in[stk[top-1]]);
				top--;
			}
			//特判根节点未被统计 
			if(top==2)
			{
				if(tlca==1) ans+=query(in[stk[2]]);//1为虚树根 
				else ans+=query(in[stk[2]])-query(in[fa[stk[2]][0]]);//1不为虚树根 
			}
			else if(cnt>=2&&tlca==1) ans+=query(in[1]);//不知道这句话是不是对的 
			printf("%lld\n",ans);
		}
		else
		{
			int pos=rd(),v=rd();
			modify(in[pos],v-val[pos]);
			modify(out[pos]+1,val[pos]-v);
			val[pos]=v;
		}
	}
	return 0;
}
/*
4 4 
10 5 2 2
1 2
1 3
1 4
Q 3 4 0
C 3 200
Q 3 4 0
Q 1 2 0
*/
2020/5/16 16:25
加载中...