[UOJ]#58. 【WC2013】糖果公园 树上带修改莫队

Candyland 有一座糖果公园,公园里不仅有美丽的风景、好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园玩。

糖果公园的结构十分奇特,它由 nn 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 11 至 nn。有 n−1n−1 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点。

糖果公园所发放的糖果种类非常丰富,总共 mm 种,它们的编号依次为 11 至 mm。每一个糖果发放处都只发放某种特定的糖果,我们用 cici 来表示 ii 号游览点的糖果。

来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的游览点出发前往另一个特定的游览点,并游览途中的景点,这条路线一定是唯一的。他们经过每个游览点,都可以品尝到一颗对应种类的糖果。

大家对不同类型的糖果的喜爱程度都不尽相同。根据游客们的反馈打分,我们得到了糖果的美味指数,第 ii 种糖果的美味指数为 vivi。另外,如果一位游客反复地品尝同一种类的糖果,他肯定会觉得有一些腻。根据量化统计,我们得到了游客第 ii 次品尝某类糖果的新奇指数 wiwi,如果一位游客第 ii 次品尝第 jj 种糖果,那么他的愉悦指数 HH 将会增加对应的美味指数与新奇指数的乘积,即 vjwivjwi。这位游客游览公园的愉悦指数最终将是这些乘积的和。

当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 mm 种中的一种),这样的目的是能够让游客们总是感受到惊喜。

糖果公园的工作人员小 A 接到了一个任务,那就是根据公园最近的数据统计出每位游客游玩公园的愉悦指数。但数学不好的小 A 一看到密密麻麻的数字就觉得头晕,作为小 A 最好的朋友,你决定帮他一把。

输入格式
第一行包含三个正整数 n,m,qn,m,q,分别表示游览点个数、糖果种类数和操作次数。

第二行包含 mm 个正整数 v1,v2,…,vmv1,v2,…,vm。

第三行包含 nn 个正整数 w1,w2,…,wnw1,w2,…,wn。

第四行到第 n+2n+2 行,每行包含两个正整数 ai,biai,bi,表示这两个游览点之间有路径可以直接到达。

第 n+3n+3 行包含 nn 个正整数 c1,c2,…,cnc1,c2,…,cn。

接下来 qq 行,每行包含三个整数 t,x,yt,x,y,表示一次操作:

若 tt 为 00,则 1≤x≤n1≤x≤n,1≤y≤m1≤y≤m,表示编号为 xx 的游览点发放的糖果类型改为 yy;

若 tt 为 11,则 1≤x,y≤n1≤x,y≤n,表示对出发点为 xx,终止点为 yy 的路线询问愉悦指数。

输出格式
按照输入的先后顺序,对于每个 tt 为 11 的操作输出一行,用一个正整数表示答案。

题解:

树上带修改莫队裸题。

代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
const int maxn=100010;
struct Edge{int y,next;}e[maxn*2];
int last[maxn],len=0;
void ins(int x,int y)
{
    int t=++len;
    e[t].y=y;e[t].next=last[x];last[x]=t;
}
int dep[maxn],fa[maxn][20];
int sta[maxn],top=0,bl[maxn],MX,cnt=0;
int n,m,a[maxn],c[maxn],block[320],l[320],r[320],vis[maxn];
LL v[maxn],w[maxn],Ans=0,ans[maxn];
void dfs(int x)
{
    dep[x]=dep[fa[x][0]]+1;
    for(int i=1;(1<<i)<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
    int now=top;
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(y==fa[x][0])continue;
        fa[y][0]=x;dfs(y);
        if(top-now>MX)
        {
            cnt++;
            while(top>now)bl[sta[top--]]=cnt;
        }
    }
    sta[++top]=x;
    if(x==1)
    {
        cnt++;
        while(top)bl[sta[top--]]=cnt;
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);//x jump
    for(int i=18;i>=0;i--)
    if((1<<i)<=dep[x]-dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=18;i>=0;i--)
    if(dep[x]>=(1<<i)&&fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
void add(int x){Ans+=w[++c[a[x]]]*v[a[x]];}
void del(int x){Ans-=w[c[a[x]]--]*v[a[x]];}
void change(int x,int y)
{
    if(vis[x])del(x),a[x]=y,add(x);
    else a[x]=y;
}
void rev(int x)
{
    if(vis[x])vis[x]=0,del(x);
    else vis[x]=1,add(x);
}
struct Modify{int x,y,pre;}A[maxn];
struct Query{int x,y,time,id;}B[maxn];
bool cmp(Query a,Query b)
{
    if(bl[a.x]!=bl[b.x])return bl[a.x]<bl[b.x];
    if(bl[a.y]!=bl[b.y])return bl[a.y]<bl[b.y];
    return a.time<b.time;
}
int la=0,lb=0,yl[maxn];
int main()
{
    int q;
    scanf("%d%d%d",&n,&m,&q);
    MX=(int)pow(1.0*n*n,1.0/3);
    for(int i=1;i<=m;i++)scanf("%lld",&v[i]);
    for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        ins(u,v);ins(v,u);
    }
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),yl[i]=a[i];
    dfs(1);
    for(int i=1;i<=q;i++)
    {
        int op,x,y;
        scanf("%d%d%d",&op,&x,&y);
        if(op==0)
        {
            A[++la].x=x;A[la].y=y;
            A[la].pre=a[x];
            a[x]=y;
        }
        else
        {
            B[++lb].x=x;B[lb].y=y;
            B[lb].time=la;B[lb].id=lb;
        }
    }
    sort(B+1,B+1+lb,cmp);
    for(int i=1;i<=n;i++)a[i]=yl[i];
    int L=1,R=1;B[0].time=0;
    for(int i=1;i<=lb;i++)
    {
        int l1=LCA(B[i].x,L);
        int l2=LCA(B[i].y,R);
        for(int x=L;x!=l1;x=fa[x][0])rev(x);
        for(int x=B[i].x;x!=l1;x=fa[x][0])rev(x);
        for(int x=R;x!=l2;x=fa[x][0])rev(x);
        for(int x=B[i].y;x!=l2;x=fa[x][0])rev(x);
        L=B[i].x;R=B[i].y;
        int l=LCA(L,R);
        rev(l);
        for(int j=B[i-1].time+1;j<=B[i].time;j++)change(A[j].x,A[j].y);
        for(int j=B[i-1].time;j>B[i].time;j--)change(A[j].x,A[j].pre);
        ans[B[i].id]=Ans;
        rev(l);
    }
    for(int i=1;i<=lb;i++)
    printf("%lld\n",ans[i]);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值