jzoj 5908.【NOIP2018模拟10.16】开荒 虚树+树状数组

Description

题目背景:
尊者神高达作为一个萌新,在升级路上死亡无数次后被一只大黄叽带回了师门。他加入师门后发现有无穷无尽的师兄弟姐妹,这几天新副本开了,尊者神高达的师门作为一个 pve师门,于是他们决定组织一起去开荒。

题目描述:
师门可以看做以 1 为根的一棵树,师门中的每一个人都有一定的装备分数。一共会有 q 个事件。每个事件可能是一次开荒,也可能是因为开荒出了好装备而导致一个人的装分出现了变化。对于一次开荒,会有 k 个人组织,由于师门的号召力很强,所以所有在组织者中任意两个人简单路径上的人都会参加。

Input

第一行 n ,q;
接下来 1 行 n 个数,代表每个人的分值;
接下来 n-1 行 u,v 代表一条边
接下来 q 行
Q 代表询问,接下来 k 个数代表组织的人数,读入为 0时停止读入。
C 代表修改,输入 x,w 代表将 x 的分值变为 w

Output

共 Q 的数量行,为开荒的人的总分值

Sample Input

4 4
10 5 2 2
1 2
2 3
2 4
Q 3 4 0
C 3 200
Q 3 4 0
Q 1 4 0

Sample Output

9
207
17

样例解释:
第一次询问,参加的人有 2,3,4 5+2+2=9
第一次修改,权值为 10 5 200 2
第二次询问,参加的人有 2,3,4 5+200+2=207
第三次询问,参加的人有 1,2,4 10+5+2=17

Data Constraint

数据范围:
20%的数据 n<=10000,q<=500;
另外 20%的数据 k=2
另外 20%的数据 没有修改操作
所有数据 n,q<=100000,所有询问 k 的和<=1000000

分析:
求距离可以不用树链剖分,可以用求出深度和lca的方法求。而修改一个点相当于修改区间,然后单点查询,树状数组可以完美解决。然后对于虚树上的非父亲节点,直接求出他到父亲的点权和(不包括父亲),而对于父亲则直接加上自己的点权即可。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long

const int maxn=1e5+7;

using namespace std;

int n,m,x,y,cnt,tot,top;
int ls[maxn],dfn[maxn],last[maxn],f[maxn][20],b[maxn],dep[maxn],q[maxn],pre[maxn],p[maxn];
LL ans,t[maxn],a[maxn],w;
char op[3];

struct edge{
	int y,next;
}g[maxn*2];

void add(int x,int y)
{
	g[++cnt]=(edge){y,ls[x]};
	ls[x]=cnt;
}

void dfs(int x,int fa)
{
	dfn[x]=last[x]=++cnt;
	f[x][0]=fa;
	dep[x]=dep[fa]+1;
	for (int i=ls[x];i>0;i=g[i].next)
	{
		int y=g[i].y;
		if (y==fa) continue;
		dfs(y,x);
		last[x]=last[y];
	}
}

bool cmp(int a,int b)
{
	return dfn[a]<dfn[b];
}

void updata(int x,LL k)
{
	for (int i=x;i<=n;i+=i&(-i)) t[i]+=k;
}

LL getsum(int x)
{
	LL sum=0;
	for (int i=x;i>0;i-=i&(-i)) sum+=t[i];
	return sum;
}

void ins(int l,int r,LL k)
{
	updata(l,k);
	if (r!=n) updata(r+1,-k);
}

int getlca(int x,int y)
{
    if (dep[x]>dep[y]) swap(x,y);
    int d=dep[y]-dep[x],k=19,t=1<<k;
    while (d)
    {
        if (d>=t) d-=t,y=f[y][k];
        t/=2,k--;
    }
    if (x==y) return x;
    k=19;
    while (k>=0)
    {
        if (f[x][k]!=f[y][k])
        {
            x=f[x][k];
            y=f[y][k];
        }
        k--;
    }
    return f[x][0];
}

LL getdis(int x,int y)
{
	return getsum(dfn[y])-getsum(dfn[x]);
}

int main()
{
	freopen("kaihuang.in","r",stdin);
	freopen("kaihuang.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for (int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	cnt=0;	
	dfs(1,0);
	for (int j=1;j<20;j++)
	{
		for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
    }		
	for (int i=1;i<=n;i++) ins(dfn[i],last[i],a[i]);		
	for (int i=1;i<=m;i++)
	{
		scanf("%s",op);		
		if (op[0]=='Q')
		{	
			scanf("%d",&x);
			tot=0;
			while (x)
			{
				b[++tot]=x;
				scanf("%d",&x);
			}
			sort(b+1,b+tot+1,cmp);
			top=0,cnt=0;
			q[++top]=b[1];
			p[++cnt]=b[1];
			for (int j=2;j<=tot;j++)
            {
            	p[++cnt]=b[j];
                int d=getlca(q[top],b[j]);
                if (d==q[top]) q[++top]=b[j];
                else
                {
                    while (dep[q[top-1]]>=dep[d])
                    {
                        pre[q[top]]=q[top-1];
                        top--;
                    }
                    if (q[top]!=d)
                    {
                        pre[q[top]]=d;
                        q[top]=d;
                        p[++cnt]=d;
                    }
                    q[++top]=b[j];
                }
            }
            while (top>=1) pre[q[top]]=q[top-1],top--;                     
            ans=0;         
            for (int i=1;i<=cnt;i++)
			{
				if (pre[p[i]]) ans+=getdis(pre[p[i]],p[i]);
				          else ans+=a[p[i]];
			}						
			printf("%lld\n",ans);			
		}
		else
		{
			scanf("%d%lld",&x,&w);
			ins(dfn[x],last[x],w-a[x]);
			a[x]=w;
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值