「WC2013」 糖果公园 - 树上带修莫队

2024-01-02 07:48

本文主要是介绍「WC2013」 糖果公园 - 树上带修莫队,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目描述

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

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

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

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

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

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

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

数据范围

对于所有的数据: 1 ≤ V i , W i ≤ 1 0 6 , 1 ≤ A i , B i ≤ n , 1 ≤ C i ≤ m , W 1 , W 2 , ⋯ &ThinSpace; , W n 1\le V_i,W_i\le 10^6,1\le A_i,B_i\le n,1\le C_i\le m,W_1,W_2,\cdots,W_n 1Vi,Wi106,1Ai,Bin,1Cim,W1,W2,,Wn是非递增序列,即对于任意 1 &lt; i ≤ n 1&lt; i\le n 1<in都满足 W i ≤ W i − 1 W_i\le W_{i-1} WiWi1

其他限制条件:
数据范围

分析

对于这道题,读题都是一个巨大的难关。题意简单来说就是给你一棵树,每个点有个颜色,每次询问你一条路径求

∑ c v a l c × ∑ i = 1 c n t c w o r t h i \sum_{c}val_c\times\sum_{i=1}^{cnt_c}worth_i cvalc×i=1cntcworthi

其中 v a l val val表示该颜色的价值, c n t cnt cnt表示其出现的次数, w o r t h i worth_i worthi表示第 i i i次出现的价值,带修改。(语文不好,故题意摘自某大佬题解)

可以很容易地想到,这可以离线然后跑带修莫队。实际上也的确如此。在将树上的带修莫队之间,推荐先去做「SCOI2005」王室联邦,是简单的树分块。然后就利用其中的分块方法,将子树中大于 b l k blk blk的分为一块。将询问离线,修改同样离线,并记录在不同的数组中,记录询问时,同时记录到该询问的修改编号。对询问排序,按照 x x x端块的顺序, y y y端时间戳,和修改时间三个关键字从小到大排序,用一个指针指向最后一个执行的修改。每个询问开始处理前,先用指针通过上下变化同时更新答案,将其指向当前询问所需要的修改编号,然后再从前一个的位置更新到当前位置,再将没有处理的Lca加上,记录答案,再撤销对Lca的修改,因为Lca很烦,不想多管他,所以只让它出个场即可。最终输出答案。

还要注意一下,块的大小不能是 n \sqrt{n} n ,因为有修改操作,为了使时间复杂度平摊,所以块的大小要取 n 2 / 3 n^{2/3} n2/3。还有要开 l o n g l o n g long\ long long long。总时间复杂度为 O ( n 5 / 3 ) O(n^{5/3}) O(n5/3)。建议 O 2 O2 O2

代码

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=100005;
struct Edge {int to,nxt;}e[N<<1];//边 
struct Query {int x,y,id,hp;}q[N];//询问 
struct Change {int x,y,last;}c[N];//修改 
int h[N],cnt,hd,hq,dep[N];
int n,m,nq,num[N],f[N][25];
int V[N],W[N],blk,dfn[N];
int bel[N],tot,st[N],top;
int vis[N],mp[N];long long temp,ans[N];
//vis[i]:是否访问节点i;mp[i]:第i种糖果的数量;
//V[i]:第i种糖果的美味指数;W[i]:第i次品尝的新奇指数
//num[x]:节点x所放置的糖果类型;bel[x]:节点x所属块的编号
//dfn[x]:节点x的时间戳;f[][]为倍增预处理数组
//st[],top为栈分块时用 
int Getint() {//快读 int s=0,f=1;char ch=getchar();while (ch<'0'||ch>'9') f=(ch=='-'?-1:1),ch=getchar();while (ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*f;
}
void Putint(long long s) {//快输出 if (s<0) {putchar('-');Putint(-s);return;}if (s>9) Putint(s/10);putchar(s%10+'0');
}
void Add_Edge(int x,int y) {//加边 e[++cnt]=(Edge){y,h[x]};h[x]=cnt;
}
bool cmp(Query a,Query b) {//排序比较方式 if (bel[a.x]!=bel[b.x])return bel[a.x]<bel[b.x];if (dfn[a.y]!=dfn[b.y])return dfn[a.y]<dfn[b.y];return a.hp<b.hp;
}
void Read() {//读入 n=Getint(),m=Getint(),nq=Getint();for (int i=1;i<=m;i++)V[i]=Getint();for (int i=1;i<=n;i++)W[i]=Getint();for (int i=1;i<n;i++) {int u,v;u=Getint();v=Getint();Add_Edge(u,v);Add_Edge(v,u); }for (int i=1;i<=n;i++)num[i]=Getint();for (int i=1;i<=nq;i++) {int type,x,y;type=Getint();x=Getint();y=Getint();if (!type) {c[++hd]=(Change){x,y,num[x]};num[x]=y;} else q[++hq]=(Query){x,y,hq,hd};}
}
void Dfs(int x,int fa) {//分块用Dfs求出dfn和dep以及bel int bot=top;dfn[x]=++dfn[0];dep[x]=dep[fa]+1;for (int i=h[x];i;i=e[i].nxt) {int y=e[i].to;if (y==fa) continue;f[y][0]=x;Dfs(y,x);if (top-bot>=blk) {tot++;while (top!=bot) bel[st[top--]]=tot;}}st[++top]=x;
}
void Divide() {//分块过程 blk=pow(n,2.0/3);Dfs(1,1);tot++;while (top) bel[st[top--]]=tot;for (int j=1;(1<<j)<=n;j++)for (int i=1;i<=n;i++)f[i][j]=f[f[i][j-1]][j-1];
}
int Lca(int x,int y) {//Lca if (dep[x]<dep[y]) swap(x,y);for (int i=17;~i;i--)if (dep[f[x][i]]>=dep[y]) x=f[x][i];if (x==y) return x;for (int i=17;~i;i--)if (f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];return f[x][0];
}
void Rev(int x) {//反转,原来有的变为无,原来无的变为有,同时更新答案 if (!vis[x]) {vis[x]=1;mp[num[x]]++;temp+=(long long)W[mp[num[x]]]*V[num[x]];} else {vis[x]=0;temp-=(long long)W[mp[num[x]]]*V[num[x]];mp[num[x]]--;}
}
void Chan(int x,int y) {//更新令num[x]=yif (vis[x]) {temp-=(long long)W[mp[num[x]]]*V[num[x]];mp[num[x]]--;num[x]=y;mp[num[x]]++;temp+=(long long)W[mp[num[x]]]*V[num[x]];} else num[x]=y;
}
int Clime(int x,int y) {//爬,x与y爬到Lca(x,y) while (x!=y) {if (dep[x]>dep[y]) {Rev(x);x=f[x][0];} else {Rev(y);y=f[y][0];}}if (dep[x]<dep[y]) return x;else return y;
}
void Solve() {sort(q+1,q+hq+1,cmp);//排序 for (int i=1;i<=hq;i++) {int lca;while (hd<q[i].hp) {//处理修改,多去少补 hd++;Chan(c[hd].x,c[hd].y);}while (hd>q[i].hp) {Chan(c[hd].x,c[hd].last);hd--;}if (i==1) lca=Clime(q[i].x,q[i].y);//对第一个要特殊处理 else {Clime(q[i-1].x,q[i].x);//从前一个转到当前 Clime(q[i-1].y,q[i].y);lca=Lca(q[i].x,q[i].y);}Rev(lca);//加入Lca ans[q[i].id]=temp;//记录答案 Rev(lca);//删去Lca }
}
void Print() {for (int i=1;i<=hq;i++)Putint(ans[i]),putchar('\n');
}
int main() {Read();//读入 Divide();//分块 Solve();//解决 Print();//输出 return 0;
}

这篇关于「WC2013」 糖果公园 - 树上带修莫队的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/561782

相关文章

【最新华为OD机试E卷-支持在线评测】分糖果(100分)-多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线评测,专栏文章质量平均 94 分 最新华为OD机试目录: https://blog.

【codechef】 Prime Distance On Tree【求树上路经长度为i的路径条数】【点分治+FFT】

传送门:【codechef】 Prime Distance On Tree 点分治+FFT水题……竟然n*n爆int没发现…… 而且NTT TLE,FFT跑的超级快…… my  code: my~~code: #include <bits/stdc++.h>using namespace std ;typedef long long LL ;#define clr( a , x ) m

力扣135-分发糖果(java详细题解)

题目链接:135. 分发糖果 - 力扣(LeetCode) 前情提要: 因为本人最近都来刷贪心类的题目所以该题就默认用贪心方法来做。 贪心方法:局部最优推出全局最优。 如果一个题你觉得可以用局部最优推出全局最优,并且没有反例来反驳的话就可以用贪心来试试。 题目思路: 本题主要就是难在,有俩个维度,当前的孩子不仅要跟左边比,还要跟右边比。 可能我们一开始就想遍历孩子,让当前孩子先跟左边

【NO.15】LeetCode经典150题-135. 分发糖果

文章目录 【NO.15】LeetCode经典150题-135. 分发糖果解题:贪心 【NO.15】LeetCode经典150题-135. 分发糖果 135. 分发糖果 【困难】 n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求,给这些孩子分发糖果: 每个孩子至少分配到 1 个糖果。相邻两个孩子评分更高的孩子会获得更多的糖果。

代码随想录算法训练营第二十九天| 134. 加油站 135. 分发糖果 860.柠檬水找零 406.根据身高重建队列

134. 加油站 题目: 在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证

公园智能厕所显示剩余厕位、公厕空气数据、纸巾耗材余量

在现代化的城市公园中,智能厕所的出现为人们带来了全新的体验。它不仅提供了基本的卫生设施,还通过先进的技术手段,实现了厕位状态监测、空气数据监测以及纸巾、洗手液耗材余量的显示,极大地提升了公共厕所的服务质量。 智能厕所的厕位状态监测功能,让使用者在进入厕所前就能清楚地了解到剩余厕位的情况。通过电子显示屏,你可以一目了然地看到哪些厕位正在使用,哪些厕位空闲。这一功能极大地节省了人们寻找厕位的

F. LIS on Tree 树上根路径LIS

1 关于lower_bound // 12345 查4 应该放在4 。而不是5的位置。。所以不是uppper。 //如果1235 查4 。可以优化5 。 2 关于 ans[u] = max(j, ans[p]); 维护一个max 一定从(premax,cur)中选出来的。 3 关于 memset(st, 0x3f, sizeof st); 这个和st + 1, st + n + 1 。。我们二分选

贪心算法---分发糖果

题目: n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求,给这些孩子分发糖果: 每个孩子至少分配到 1 个糖果。相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。 思路:使用两次贪心思想。 第一次从左到右遍历,只比较右边大于左边的情况,如果ratings[i]>ratings[i-1

HDU 5016 Mart Master II (树上点分治)

题目地址:HDU 5016 先两遍DFS预处理出每个点距最近的基站的距离与基站的编号。 然后找重心,求出每个点距重心的距离,然后根据dis[x]+dis[y] < d[y],用二分找出当前子树中不会被占领的数量,总点数减去即是被占领的数量。这样就可以求出每个点最多占领的点的数量。然后找最大值即可。 代码如下: #include <iostream>#include <string.h>

HDU 4871 Shortest-path tree (最短路+树上点分治)

题目地址:HDU 4871 先用最短路求出根节点到其它各点的最短距离,然后利用最短距离DFS一下构造出最短路树,然后剩下的就是在构造出来的这棵树上做树分治,很简单的树分治。 代码如下: #include <iostream>#include <string.h>#include <math.h>#include <queue>#include <algorithm>#include