【WC2013】 糖果公园 - 树上莫队

【问题描述】

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 。这位游客游览公园的愉悦指数最终将是这些乘积的和。
当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 m 种中的一种),这样的目的是能够让游客们总是感受到惊喜。
糖果公园的工作人员小 A 接到了一个任务,那就是 根据公园最近的数据统计出每位游客游玩公园的愉悦指数 。但数学不好的小 A 一看到密密麻麻的数字就觉得头晕,作为小 A 最好的朋友,你决定帮他一把。
【输入文件】
从文件 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 为 1,则 1 ≤ x, y ≤ n,表示对出发点为 x,终止点为 y 的路线询问愉悦指数。
【输出文件】
输出到文件 park.out 中。
按照输入的先后顺序,对于每个 Type 为 1 的操作输出一行,用一个正整数
表示答案。

思路

树上莫队模板题,莫队算法详见『这里』

#include <bits/stdc++.h>
#define ri register
using namespace std;
typedef long long ll;
const int maxn = 100000 + 10;
int n,m,Q,cntq,cntc,Time,block,mark[maxn],dep[maxn],cnt[maxn],father[maxn][20],dfn[maxn],v[maxn],w[maxn],x[maxn],edgecnt,first[maxn];
ll tot,ans[maxn];
struct edge {
    int to,next;
}eg[maxn];
inline void addedge(ri int u,ri int v){
    eg[++edgecnt] = (edge){v,first[u]}; first[u] = edgecnt;
    eg[++edgecnt] = (edge){u,first[v]}; first[v] = edgecnt;
}
inline int read() {
    char ch = getchar();
    ri int x = 0, f = 1;
    while  (ch < ‘0‘ || ch > ‘9‘) {
        if (ch == ‘-‘) f = -1;
        ch = getchar();
    }
    while (‘0‘ <= ch && ch <= ‘9‘) {
        x = x * 10 + ch - ‘0‘;
        ch = getchar();
    }
    return x * f;
}
void write(ll x) {
    ri ll y = 10,len = 1;
    while (y <= x) { y *= 10; len++; }
    while (len--) { y /= 10; putchar(x/y+48); x %= y; }
}
struct Query {
    int l,r,num,c;
    inline bool operator < (Query cmp) const {
        if (dfn[l]/block != dfn[cmp.l]/block) return dfn[l]/block < dfn[cmp.l]/block;
        if (dfn[r]/block != dfn[cmp.r]/block) return dfn[r]/block < dfn[cmp.r]/block;
        return c < cmp.c;
    }
}q[maxn];
struct Change {
    int pos,to;
}c[maxn];
inline void dfs(ri int now,ri int fa) {
    father[now][0] = fa;
    dfn[now] = ++Time;
    dep[now] = dep[fa]+1;
    ri size_t i;
    for (i = first[now];~i;i = eg[i].next)
        if (eg[i].to != fa) dfs(eg[i].to,now);
}
inline void init() {
    ri int i,j;
    for (j = 1;(1<<j) <= n;j++)
        for (i = 1;i <= n;i++)
            father[i][j] = father[father[i][j-1]][j-1];
}
inline int lca(ri int a,ri int b) {
    if (dep[a] < dep[b]) swap(a,b);
    ri int i,j; for (i = 0;(1<<i) <= dep[a];i++); i--;
    for (j = i;j >= 0;j--)
        if (dep[a]-(1<<j) >= dep[b]) a = father[a][j];
    if (a == b) return a;
    for (j = i;j >= 0;j--)
        if (father[a][j] != father[b][j]) a = father[a][j],b = father[b][j];
    return father[a][0];
}
inline void reverse(ri int i) {
    if (mark[i]) { mark[i] ^= 1; tot -= (long long)w[cnt[x[i]]--]*(long long)v[x[i]]; }
    else { mark[i] ^= 1; tot += (long long)w[++cnt[x[i]]]*(long long)v[x[i]]; }
}
inline void change(ri int i) {
    if (!mark[c[i].pos]) swap(c[i].to,x[c[i].pos]);
    else {
        reverse(c[i].pos);
        swap(c[i].to,x[c[i].pos]);
        reverse(c[i].pos);
    }
}
int main() {
    memset(first,-1,sizeof(first));
    n = read(),m = read(),Q = read();
    block = pow(n,2.0/3)*0.5;
    ri int i,t,X,y;
    for (i = 1;i <= m;i++) v[i] = read();
    for (i = 1;i <= n;i++) w[i] = read();
    for (i = 1;i < n;i++) X = read(),y = read(),addedge(X,y);
    for (i = 1;i <= n;i++) x[i] = read();
    dfs(1,0);
    init();
    for (i = 1;i <= Q;i++) {
        t = read(),X = read(),y = read();
        if (!t) c[++cntc].pos = X,c[cntc].to = y;
        else q[++cntq].l = X,q[cntq].r = y,q[cntq].num = cntq,q[cntq].c = cntc;
    }
    sort(q+1,q+cntq+1);
    if (dfn[q[1].l] > dfn[q[1].r]) swap(q[1].l,q[1].r);
    ri ll LCA  = lca(q[1].l,q[1].r),curl = q[1].l,curr = q[1].r,curc = 0,l = curl,r = curr;
    while (l != LCA) reverse(l),l = father[l][0];
    while (r != LCA) reverse(r),r = father[r][0];
    while (curc < q[1].c) change(++curc);
    reverse(LCA); ans[q[1].num] = tot; reverse(LCA);
    for (i = 2;i <= cntq;i++) {
        if (dfn[q[i].l] > dfn[q[i].r]) swap(q[i].l,q[i].r);
        LCA = lca(q[i].r,curr),l = curr,r = q[i].r;
        while (l != LCA) reverse(l),l = father[l][0];
        while (r != LCA) reverse(r),r = father[r][0];
        LCA = lca(q[i].l,curl),l = curl,r = q[i].l;
        while (l != LCA) reverse(l),l = father[l][0];
        while (r != LCA) reverse(r),r = father[r][0];
        while (curc < q[i].c) change(++curc);
        while (curc > q[i].c) change(curc--);
        LCA = lca(q[i].l,q[i].r);
        reverse(LCA); ans[q[i].num] = tot; reverse(LCA);
        curl = q[i].l,curr = q[i].r;
    }
    for (i = 1;i <= cntq;i++) write(ans[i]),putchar(‘\n‘);
    return 0;
}

原文地址:https://www.cnblogs.com/lrj124/p/8641602.html

时间: 2024-08-03 17:58:26

【WC2013】 糖果公园 - 树上莫队的相关文章

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

题意:链接 方法:莫队上树 解析: 首先先考虑个莫队的算法,树分块 然后怎么做呢? 可以设两个指针么,然后按一定的排序方式移动指针,使复杂度可过. 观察到这道题是100s的,n^2是GG的,但比n^2稍微小一点就不GG了, 所以排序的作用就体现了. 按照左端点所在块为第一关键字,右端点所在块为第二关键字出现时间为第三关键字排序. 一共有n的1/3次方个块,所以所有的询问的左右端点所在的块的取值最多之后n的2/3次方种,对于每一种,对时间的修改最多会从尾改到头,所以又多乘一个q,n,q同阶,所以总

【Luogu P4074】[WC2013]糖果公园(树上带修改莫队)

题目描述 Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩. 糖果公园的结构十分奇特,它由 \(n\) 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 \(1\) 至 \(n\).有 \(n-1\) 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点. 糖果公园所发放的糖果种类非常丰富,总共有 \(m\) 种,

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

题目链接:BZOJ - 3052 题目分析 这道题就是非常经典的树上莫队了,并且是带修改的莫队. 带修改的莫队:将询问按照 左端点所在的块编号为第一关键字,右端点所在的块为第二关键字,位于第几次修改之后为第三关键字 排序. 我们将块的大小设置在 n^(2/3) ,这样一共有 n^(1/3) 个块.最后算法的总复杂度会是 n^(5/3) . 每一次如何从上一个询问转移来呢? 假设上一个询问是 (lx, ly, lt) ,这次的询问是 (x, y, t) .t 代表是在第 t 次修改操作之后. 首先

bzoj 3052: [wc2013]糖果公园(带修改的树上莫队)

3052: [wc2013]糖果公园 Time Limit: 200 Sec  Memory Limit: 512 MB Submit: 892  Solved: 425 [Submit][Status][Discuss] Description Input Output Sample Input Sample Input Sample Output 84 131 27 84 HINT Source [Submit][Status][Discuss] 题解:bzoj 2120 和 bzoj 37

BZOJ3052 [wc2013] 糖果公园 【树上莫队】

树上莫队和普通的序列莫队很像,我们把树进行dfs,然后存一个长度为2n的括号序列,就是一个点进去当作左括号,出来当作右括号,然后如果访问从u到v路径,我们可以转化成括号序列的区间,记录x进去的时候编号为f[x],出来时为g[x],然后分类讨论一下(f[u]<f[v]),如果u和v的lca不是u,那么就是从g[u]到f[v],否则就是lca的f到另一个点的f,(可以自己试一下,中间过程没有用的点正好就抵消掉了)这里要注意一下,从g[u]到f[v]的时候我们会少掉lca这个点,特殊处理一下即可,然后

【BZOJ-3052】糖果公园 树上带修莫队算法

3052: [wc2013]糖果公园 Time Limit: 200 Sec  Memory Limit: 512 MBSubmit: 883  Solved: 419[Submit][Status][Discuss] Description Input Output Sample Input Sample Output 84 131 27 84 HINT Source Solution 树上带修莫队 本质还是树上莫队,详情可以转 BZOJ-3757苹果树 但是这里需要修改,就需要一些特殊的地方

bzoj 3052: [wc2013]糖果公园 带修改莫队

3052: [wc2013]糖果公园 Time Limit: 250 Sec  Memory Limit: 512 MBSubmit: 506  Solved: 189[Submit][Status] Description Input Output Sample Input Sample Input Sample Output 84 131 27 84 HINT 本来这道题想到了莫队算法,但是看到带修改就直接放弃了.结果看题解才发现带修改居然也能用莫队!!!之所以可以这样用,是因为修改的时间复

WC2013 糖果公园

COGS 1817. [WC2013]糖果公园 http://www.cogs.pro/cogs/problem/problem.php?pid=1817 ★★★☆   输入文件:park.in   输出文件:park.out   简单对比时间限制:8 s   内存限制:512 MB [题目描述] Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园玩. 糖果公园的结构十分奇特,它由 n 个游览点构成,每个游览点

[bzoj 3052][wc2013]糖果公园

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3052 [wc2013]糖果公园 Time Limit: 200 Sec  Memory Limit: 512 MBSubmit: 1213  Solved: 609[Submit][Status][Discuss] Description Input Output Sample Input Sample Input Sample Output 84 131 27 84 树上莫队,把树分块,