BZOJ 3052: [wc2013]糖果公园 树上莫队

title

BZOJ 3052

LUOGU 4074

Description

Candyland 有一座糖果公园,公园里不仅有美丽的风景、好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩。
糖果公园的结构十分奇特,它由 n 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 1 至 n。有 n – 1 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点。
糖果公园所发放的糖果种类非常丰富,总共有 m 种,它们的编号依次为 1 至 m。每一个糖果发放处都只发放某种特定的糖果,我们用 \(C_i\) 来表示 \(i\) 号游览点的糖果。
来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的游览点出发前往另一个特定的游览点,并游览途中的景点,这条路线一定是唯一的。他们经过每个游览点,都可以品尝到一颗对应种类的糖果。
大家对不同类型糖果的喜爱程度都不尽相同。 根据游客们的反馈打分,我们得到了糖果的美味指数, 第 \(i\) 种糖果的美味指数为 \(V_i\)。另外,如果一位游客反复地品尝同一种类的糖果,他肯定会觉得有一些腻。根据量化统计,我们得到了游客第 i 次品尝某类糖果的新奇指数 \(W_i\)。如果一位游客第 i 次品尝第 j 种糖果,那么他的愉悦指数 H 将会增加对应的美味指数与新奇指数的乘积,即 \(V_j×W_i\) 。这位游客游览公园的愉悦指数最终将是这些乘积的和。
当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 mm 种中的一种),这样的目的是能够让游客们总是感受到惊喜。
糖果公园的工作人员小 A 接到了一个任务,那就是根据公园最近的数据统计出每位游客游玩公园的愉悦指数。但数学不好的小 A 一看到密密麻麻的数字就觉得头晕,作为小 A 最好的朋友,你决定帮他一把。

Input

从文件 park .in 中读入数据。
第一行包含三个正整数 n, m, q, 分别表示游览点个数、 糖果种类数和操作次数。
第二行包含 m 个正整数 \(V_1, V_2 , …, V_m\)
第三行包含 n 个正整数 \(W_1, W_2 , …, W_n\)
第四行到第 n + 2 行,每行包含两个正整数 \(A_i , B_i\),表示这两个游览点之间有路径可以直接到达。
第 n + 3 行包含 n 个正整数 \(C_1, C_2 , …, C_n\)
接下来 q 行, 每行包含三个整数 Type,x, y,表示一次操作:
若 Type 为 0,则 1 ≤ x ≤ n, 1 ≤ y ≤ m,表示将编号为 x 的游览点发放的糖果类型改为 y;
若 Type 为 11,则 1 ≤ x, y≤ n,表示对出发点为 x,终止点为 y 的路线询问愉悦指数。

Output

输出到文件 park.out 中。
按照输入的先后顺序,对于每个 Type 为 1 的操作输出一行,用一个正整数表示答案。

Sample 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

Sample Output

84
131
27
84

HINT

我们分别用
QQ20180112220959.png
代表 \(C_i\) 为 1、 2、 3 的节点,在修改之前:
QQ20180112221024.png
在将 \(C_2\) 修改为 1 之后:
QQ20180112221106.png
【数据规模与约定】
对于所有的数据: \(1 ≤ V_i, W_i≤ 10^6,1 ≤ A_i , B_i≤ n, 1 ≤ C_i≤ m, W_1, W_2, …, W_n\) 是非递增序列,即对任意 \(1 < i ≤ n\), 满足 \(W_i\)
其它的限制条件如下表所示:
在这里插入图片描述

analysis

觉得这位亲 g1n0st 说的树上莫队挺简单的,就转过来了:

把一棵树变成一条序列,然后直接用莫队做就可以了,比如说下面这棵树:
在这里插入图片描述
我们把它整理成括号序的形式为:521134432665(也就是在\(dfs\)遍历树的时候,将每个结点进栈时记录一次,出栈时记录一次)。

如果要询问\(a\to b\)之间的信息,需要分类讨论:

如果 \(a\)\(b\) 的祖先,所求信息即为 \(a,b\) 最后出现位置之间的信息。

如果 \(a\) 不是 \(b\) 的祖先,所求信息即为 \(a\) 最先出现的位置以及 \(b\) 最后出现的位置之间的信息再加上 \(lca(a,b)\) 上的信息。

注意有些节点可能会在括号序中出现两次,说明这个节点在这段过程中入栈后又弹出了,不能计入所求信息(处理的话开个数组异或一下就好了)。

比如说要求\(4\to 6\)之间的信息,这一段信息为 括号序 4326 再加上\(lca(4,6)=5\)

然后把剩下的事情交给莫队……

所以:套用一下神犇同学blng的换元法题解

树上莫队=树上分块 + 莫队思想。
树上分块=树上操作 + 分块思想。
树上操作=\(dfs\)序 + \(lca\)倍增

哦,对,放参考资料:《全网最详细、最深的四类莫队算法讲解》

本人在写这道题时,被 \(LCA\) 卡死,下面展示两种 \(LCA\)\(code\)

inline int lca(int x,int y)
{
    if (d[x]>d[y]) swap(x,y);
    for (int i=16; i>=0; --i)
        if (d[y]-(1<<i)>=d[x]) y=f[y][i];//
    if (x==y) return x;
    for (int i=16; i>=0; --i)
        if (f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
inline int lca(int x,int y)
{
    if (d[x]>d[y]) swap(x,y);
    for (int i=16; i>=0; --i)
        if (d[f[y][i]]>=d[x]) y=f[y][i];//
    if (x==y) return x;
    for (int i=16; i>=0; --i)
        if (f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}

第 1 种可\(AC\),第 2 种就挂了,我也不明白到底有什么区别,可能学了个假的\(LCA\)吧。

有哪位大佬路过,烦请解答疑问,感激不尽。

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}

template<typename T>inline void write(T x)
{
    if (!x) { putchar('0'); return ; }
    if (x<0) putchar('-'), x=-x;
    T num=0, ch[20];
    while (x) ch[++num]=x%10+48, x/=10;
    while (num) putchar(ch[num--]);
}

int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
    ver[++len]=y,Next[len]=head[x],head[x]=len;
}

int d[maxn],f[maxn][17];
int In[maxn],Out[maxn],dfn[maxn<<1],id;
inline void dfs(int x)
{
    dfn[ In[x]=++id ]=x;
    for (int i=1; i<=16; ++i) f[x][i]=f[f[x][i-1]][i-1];
    for (int i=head[x]; i; i=Next[i])
    {
        int y=ver[i];
        if (y==f[x][0]) continue;
        f[y][0]=x;
        d[y]=d[x]+1;
        dfs(y);
    }
    dfn[ Out[x]=++id ]=x;
}

inline int lca(int x,int y)
{
    if (d[x]>d[y]) swap(x,y);
    for (int i=16; i>=0; --i)
        if (d[y]-(1<<i)>=d[x]) y=f[y][i];//
    if (x==y) return x;
    for (int i=16; i>=0; --i)
        if (f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}

struct Orz
{
    int l,r,t,id;
    ll ans;
}q[maxn];
int belong[maxn<<1];
inline bool cmp1(Orz a,Orz b)
{
    return belong[a.l]^belong[b.l]?a.l<b.l:(belong[a.r]^belong[b.r]?a.r<b.r:a.t<b.t);
}

inline bool cmp2(Orz a,Orz b)
{
    return a.id<b.id;
}

int w[maxn],v[maxn],cnt[maxn],col[maxn];
bool vis[maxn];
ll ans;
inline void revise(int pos)
{
    if (vis[pos]) ans-=1ll*w[cnt[col[pos]]--]*v[col[pos]];
    else ans+=1ll*w[++cnt[col[pos]]]*v[col[pos]];
    vis[pos]^=1;
}

inline void doit(int pos,int color)
{
    if (vis[pos]) revise(pos),col[pos]=color,revise(pos);
    else col[pos]=color;
}

struct QWQ{int pos,Old,New;}c[maxn];
int Tim,T,now[maxn];
int main()
{
    int n,m,cas;
    read(n);read(m);read(cas);
    for (int i=1; i<=m; ++i) read(v[i]);
    for (int i=1; i<=n; ++i) read(w[i]);
    for (int i=1,x,y; i<n; ++i) read(x),read(y),add(x,y),add(y,x);
    for (int i=1; i<=n; ++i) read(col[i]),now[i]=col[i];

    dfs(1);
    int block=pow(n,2.0/3.0);
    for (int i=1; i<=id; ++i) belong[i]=(i-1)/block+1;
    while (cas--)
    {
        int x,y,opt;read(opt);read(x);read(y);
        if (opt)
        {
            if (In[x]>In[y]) swap(x,y);
            q[++T]=(Orz){(lca(x,y)==x)?In[x]:Out[x],In[y],Tim,T};
        }
        else c[++Tim]=(QWQ){x,now[x],y},now[x]=y;
    }

    int l=1,r=0,t=0;
    sort(q+1,q+T+1,cmp1);
    for (int i=1; i<=T; ++i)
    {
        while (t<q[i].t) ++t,doit(c[t].pos,c[t].New);
        while (t>q[i].t) doit(c[t].pos,c[t].Old),--t;

        while (l<q[i].l) revise(dfn[l++]);
        while (l>q[i].l) revise(dfn[--l]);
        while (r<q[i].r) revise(dfn[++r]);
        while (r>q[i].r) revise(dfn[r--]);

        int x=dfn[l],y=dfn[r],tmp=lca(x,y);
        if (x!=tmp && y!=tmp) revise(tmp),q[i].ans=ans,revise(tmp);
        else q[i].ans=ans;
    }

    sort(q+1,q+T+1,cmp2);
    for (int i=1; i<=T; ++i) write(q[i].ans),puts("");
    return 0;
}

转载于:https://www.cnblogs.com/G-hsm/p/11348083.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值