[BZOJ 3052] [Luogu P4074] [WC2013]糖果公园

10 篇文章 0 订阅
7 篇文章 0 订阅
BZOJ传送门 (权限题)
洛谷传送门

题目描述

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 将会增加对应的美味指数与新奇指数的乘积,即 Vj×Wi。这位游客游览公园的愉悦指数最终将是这些乘积的和。

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

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

输入输出格式

输入格式:

从文件 park.in p a r k . i n 中读入数据。

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

第二行包含 m 个正整数 V1 V 1 , V2 V 2 , …, Vm V m

第三行包含 n n 个正整数 W1, W2 W 2 , …, Wn W n

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

n+3 n + 3 行包含 n n 个正整数 C1, C2 C 2 , …, Cn C n

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

Type T y p e 0 0 ,则 1xn 1ym 1 ≤ y ≤ m ,表示将编号为 x x 的游览点发放的糖果类型改为 y

Type T y p e 1 1 ,则 1x,yn ,表示对出发点为 x x ,终止点为 y 的路线询问愉悦指数。

输出格式:

输出到文件 park.out p a r k . o u t 中。

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

输入输出样例

输入样例#1:
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

输出样例#1:

84
131
27
84

解题分析

此题看起来很难, 其实就是将待修改莫队和树上莫队混合在一起, 并且不需要线段树维护。 如果不会的朋友可以看博主的另外两篇博客:[BZOJ 3757] 苹果树[Luogu P1903] [国家集训队]数颜色

这两种莫队打熟了这道题还是很简单的…. 博主1A 233
代码如下:

#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <cmath>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100005
#define ll long long
template <class T>
IN void in (T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int head[MX], son[MX], fat[MX], topf[MX], siz[MX], col[MX];
int cot[MX], bonus[MX], taste[MX], seg[MX], dep[MX], st[MX];
int dot, q, cnt, kind, block, que, opr, tot, top;
bool vis[MX];
ll res, ans[MX];
struct Edge
{
    int to, nex;
}edge[MX << 1];
IN void addedge(const int &from, const int &to)
{
    edge[++cnt] = {to, head[from]};
    head[from] = cnt;
}
struct Que
{
    int lef, rig, aft, id;
}eve[MX];
struct Modify
{
    int pos, col;
}mod[MX];
IN bool operator < (const Que &x, const Que &y)
{//同样的奇偶分块优化
    if(seg[x.lef] != seg[y.lef]) return seg[x.lef] < seg[y.lef];
    if(seg[x.rig] != seg[y.rig]) return (seg[x.lef] & 1) ? seg[x.rig] < seg[y.rig] : seg[x.rig] > seg[y.rig];
    return (seg[x.lef] & 1) ? x.aft < y.aft : x.aft > y.aft;
}
void DFS(const int &now)
{//分块, 确定子树大小、点深度、重儿子和父节点
    int bot = top;
    siz[now] = 1;
    for (int i = head[now]; i; i = edge[i].nex)
    {
        if(dep[edge[i].to]) continue;
        fat[edge[i].to] = now;
        dep[edge[i].to] = dep[now] + 1;
        DFS(edge[i].to);
        siz[now] += siz[edge[i].to];
        if(siz[edge[i].to] > siz[son[now]]) son[now] = edge[i].to;
        if(top - bot >= block) {++tot; W (top > bot) seg[st[top--]] = tot;}
    }
    st[++top] = now;
}
void DFS(const int &now, const int &grand)
{//确定重链顶端
    topf[now] = grand;
    if(!son[now]) return;
    DFS(son[now], grand);
    for (R int i = head[now]; i; i = edge[i].nex)
    {
        if(topf[edge[i].to]) continue;
        DFS(edge[i].to, edge[i].to);
    }
}
IN int LCA(R int x, R int y)//树剖LCA
{
    W (topf[x] != topf[y])
    {
        if(dep[topf[x]] < dep[topf[y]]) std::swap(x, y);
        x = fat[topf[x]];
    }
    return dep[x] < dep[y] ? x : y;
}
IN void modify(const int &now)//修改单点计算情况
{
    if(!vis[now]) {++cot[col[now]]; res += 1ll * taste[col[now]] * bonus[cot[col[now]]];}
    else {--cot[col[now]]; res -= 1ll * taste[col[now]] * bonus[cot[col[now]] + 1];}
    vis[now] ^= 1;
}
IN void change(const int &num)//单点修改颜色
{
    if(vis[mod[num].pos])
    {
        --cot[col[mod[num].pos]], res -= 1ll * taste[col[mod[num].pos]] * bonus[cot[col[mod[num].pos]] + 1];
        ++cot[mod[num].col], res += 1ll * taste[mod[num].col] * bonus[cot[mod[num].col]];
    }
    std::swap(col[mod[num].pos], mod[num].col);
}
IN void cal(R int x, R int y)//暴力跳链
{
    W (x ^ y)
    {
        if(dep[x] > dep[y]) modify(x), x = fat[x];
        else modify(y), y = fat[y];
    }
}
int main(void)
{
    int a, b, lca, deal = 0;
    in(dot), in(kind), in(q); block = std::pow(dot, 2.0 / 3) + 1;
    for (R int i = 1; i <= kind; ++i) in(taste[i]);
    for (R int i = 1; i <= dot; ++i) in(bonus[i]);
    for (R int i = 1; i < dot; ++i) in(a), in(b), addedge(a, b), addedge(b, a);
    dep[1] = fat[1] = 1;
    DFS(1);
    W (top) seg[st[top--]] = tot;
    DFS(1, 1);
    for (R int i = 1; i <= dot; ++i) in(col[i]);
    for (R int i = 1; i <= q; ++i)
    {
        in(a);
        if(a)
        {
            ++que, in(eve[que].lef), in(eve[que].rig), eve[que].id = que, eve[que].aft = opr; 
            if(seg[eve[i].lef] > seg[eve[i].rig]) std::swap(eve[i].lef, eve[i].rig);
        }
        else ++opr, in(mod[opr].pos), in(mod[opr].col);
    }
    std::sort(eve + 1, eve + 1 + que);
    cal(eve[1].lef, eve[1].rig);
    lca = LCA(eve[1].lef, eve[1].rig);
    modify(lca); 
    W (eve[1].aft > deal) change(++deal);
    W (eve[1].aft < deal) change(deal--);
    ans[eve[1].id] = res;
    modify(lca);
    int lb = eve[1].lef, rb = eve[1].rig;
    for (R int i = 2; i <= que; ++i)
    {
        cal(lb, eve[i].lef), cal(rb, eve[i].rig);
        lca = LCA(eve[i].lef, eve[i].rig);
        modify(lca);
        W (eve[i].aft > deal) change(++deal);
        W (eve[i].aft < deal) change(deal--);
        ans[eve[i].id] = res;
        modify(lca);
        lb = eve[i].lef, rb = eve[i].rig;
    }
    for (R int i = 1; i <= que; ++i) printf("%lld\n", ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值