bzoj3052&&uoj58 [wc2013]糖果公园(带修改的树上莫队)

题目链接

题目描述

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

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

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

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

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

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

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

输入格式

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

第二行包含 m m 个正整数v1,v2,,vm

第三行包含 n n 个正整数w1,w2,,wn

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

n+3 n + 3 行包含 n n 个正整数c1,c2,,cn

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

t t 0,则 1xn1xn1ym1ym 1 ≤ x ≤ n 1 ≤ x ≤ n , 1 ≤ y ≤ m 1 ≤ y ≤ m ,表示编号为 x x 的游览点发放的糖果类型改为y

t t 1,则 1x,yn1x,yn 1 ≤ x , y ≤ n 1 ≤ x , y ≤ n ,表示对出发点为 x x ,终止点为y的路线询问愉悦指数。

输出格式

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

样例一
Input

4 3 5
1 9 2
7 6 5 1
2 3
3 1
3 4
1 2 3 2
1 1 2
1 4 2
0 2 1
1 1 2
1 4 2

Output

84
131
27
84

Hint

这里写图片描述

分析:
带修改的树上莫队
既然我们知道了树上莫队怎么搞
也知道了带修改的序列莫队怎么破
那么我们直接把这两个结合起来即可

我们还是处理出 dfs d f s

  • lca(u,v)=u||v>[st[u],st[v]] l c a ( u , v ) = u | | v − > [ s t [ u ] , s t [ v ] ]
  • lca(u,v)=p>[ed[u],st[v]]+p l c a ( u , v ) = p − > [ e d [ u ] , s t [ v ] ] + p

记录每个询问之前的修改次数
每次处理询问之前暴力移动修改标记
如果我们要修改结点 v v ,而当前区间中的vis[v]=1
说明 v v 可以影响当前询问,我们就需要修改一下

我一开始看见题目描述中有起点和终点,吓了一跳,还需要规定行走方向啊
后来仔细一想,不管行走方向是什么样,我们最后统计出来的贡献肯定都是一样的

tip

分块大小为n23最快

询问中的左右端点是 dfs d f s 序上的,但是修改的时候都是结点编号,不要搞错了

对于贡献的计算,想清楚就好
注意空间( dfs d f s 序的长度为 2n 2 n ,空间开小RE了一发)
最后的答案要开ll

#include<bits/stdc++.h>
#define ll long long

using namespace std;

const int N=200010;
int n,m,Q,cnt_q=0,cnt_c=0;
int v[N],w[N],C[N],cnt[N],vis[N];
ll tmp,ans[N];
struct node{
    int x,y,p,block,num,id;
}q[N];
struct point{
    int x,y;
}c[N];
struct E{
    int y,nxt;
}way[N<<1];
int st[N],tot=0,dfn[N],clo=0,in[N],out[N],pre[N][20],deep[N];

void add(int u,int w) {
    tot++;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
    tot++;way[tot].y=u;way[tot].nxt=st[w];st[w]=tot;
}

int cmp(const node &a,const node &b) {
    if (a.block!=b.block) return a.block<b.block;
    else if (a.y!=b.y) return a.y<b.y;
    else return a.num<b.num;
}

void dfs(int now,int fa,int dep) {
    pre[now][0]=fa;
    deep[now]=dep;
    for (int i=1;i<20;i++) {
        if ((1<<i)>dep) break;
        pre[now][i]=pre[pre[now][i-1]][i-1];
    }
    dfn[++clo]=now; 
    in[now]=clo;
    for (int i=st[now];i;i=way[i].nxt)
        if (way[i].y!=fa) 
            dfs(way[i].y,now,dep+1);
    dfn[++clo]=now;
    out[now]=clo;
}

int LCA(int x,int y) {
    if (deep[x]<deep[y]) swap(x,y);
    int d=deep[x]-deep[y];
    if (d)
        for (int i=0;i<20&&d;i++,d>>=1)
            if (d&1)
                x=pre[x][i];
    if (x==y) return x;
    for (int i=19;i>=0;i--)
        if (pre[x][i]!=pre[y][i]) {
            x=pre[x][i];
            y=pre[y][i];
        }
    return pre[x][0];
}

void update(int x) {
    int co=C[x];
    if (vis[x]) {            //删除影响
        tmp-=(ll)w[cnt[co]]*v[co];
        cnt[co]--;
    }
    else {
        cnt[co]++;
        tmp+=(ll)w[cnt[co]]*v[co];
    }
    vis[x]^=1;
}

void change(int x,int l,int r) {
    bool flag=0;
    if (vis[c[x].x]) update(c[x].x),flag=1;     //这个结点在序列中 

    swap(C[c[x].x],c[x].y);

    if (flag) update(c[x].x);
}

void solve() {
    int l=1,r=0,now=0;
    tmp=0;
    for (int i=1;i<=cnt_q;i++) {
        while (q[i].y<r) update(dfn[r]),r--;
        while (q[i].y>r) r++,update(dfn[r]);
        while (q[i].x<l) l--,update(dfn[l]);
        while (q[i].x>l) update(dfn[l]),l++;

        while (now<q[i].num) now++,change(now,l,r);
        while (now>q[i].num) change(now,l,r),now--;

        if (q[i].p) update(q[i].p);                     //lca
        ans[q[i].id]=tmp;
        if (q[i].p) update(q[i].p);
    }
}

int main()
{
    scanf("%d%d%d",&n,&m,&Q);
    for (int i=1;i<=m;i++) scanf("%d",&v[i]);
    for (int i=1;i<=n;i++) scanf("%d",&w[i]);
    for (int i=1;i<n;i++) {
        int u,w;
        scanf("%d%d",&u,&w);
        add(u,w);
    }
    dfs(1,0,1);
    for (int i=1;i<=n;i++) scanf("%d",&C[i]);

    int unit=(int)pow(n,2.0/3);
    int opt,x,y;
    for (int i=1;i<=Q;i++) {
        scanf("%d%d%d",&opt,&x,&y);
        if (opt==0) {
            cnt_c++;
            c[cnt_c].x=x; c[cnt_c].y=y; 
        }
        else {
            cnt_q++;
            if (in[x]>in[y]) swap(x,y);
            int p=LCA(x,y);
            if (p==x||p==y) {
                q[cnt_q].x=in[x];
                q[cnt_q].y=in[y];
                q[cnt_q].p=0;
                q[cnt_q].block=(q[cnt_q].x-1)/unit+1;
                q[cnt_q].num=cnt_c;
                q[cnt_q].id=cnt_q;
            }
            else {
                q[cnt_q].x=out[x];
                q[cnt_q].y=in[y];
                q[cnt_q].p=p;
                q[cnt_q].block=(q[cnt_q].x-1)/unit+1;
                q[cnt_q].num=cnt_c;
                q[cnt_q].id=cnt_q;
            }
        }
    }

    sort(q+1,q+1+cnt_q,cmp);
    solve();
    for (int i=1;i<=cnt_q;i++) printf("%lld\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值