BZOJ3052——糖果公园
0、题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3052
1、题目大意:给定一颗n个点的无根树,每个点有一个颜色,要进行q次操作,有两种操作,颜色总数是m。
a) Query操作,给定起始点和终点,对于这条路径,从起始点出发,对于沿途的点,如果这个点的颜色j是第i次出现,那么对于这个询问的答案的贡献是vi∗wj。
b) Change操作,每次修改一个节点的颜色。
——截图来自uoj
2、解题思路:
a)这道题在BZOJ上的时间限制是200s,那么对于前3个点,我们可以每次查询在树上dfs,修改直接改颜色。时间复杂度O(n2),期望得分30。
b)对于第四个和第五个点,我们发现m相对较小而且给出的是一个链,还没有修改操作,那么我们可以将这个链看成一个序列,并对这个序列进行分块,每块的大小是,考虑莫队算法,我们将需要查询的区间排序,第一关键字按照左端点的块,第二关键字我们按照右端点的编号排序,每次我们进行暴力的转移,这样,时间复杂度是O(n−−√),为什么呢?因为转移分两种,要么都在这个n−−√的块中转移,转移n次,时间复杂度是O(n−−√),要么是这个块中转移到另外一个块,每次转移是O(n),最多会有n−−√次转移,时间复杂度也是O(n−−√),于是总体的时间复杂度是O(n−−√)。
i.说了这么多,也没个代码= =,莫队算法模板题:BZOJ2038 小Z的袜子
sort(q + 1, q + m + 1, cmp); //对询问进行排序
for(int i = 1; i <= m; i ++){
while(r < q[i].r) {
//右端点向右转移,更新答案
}
while(r > q[i].r) {
//右端点向左转移,更新答案
}
//实际上就是将r->q[i].r
while(l < q[i].l) {
//左端点向左转移,更新答案
}
while(l > q[i].l) {
//左端点向右转移,更新答案
}
//实际上就是将l->q[i].l
//计算这次询问的答案
}
加上a)算法,期望得分50。
c)对于第六和第七个点,我们能看出和b)解法唯一区别就是给点的树不是链了,那么b)中的算法能不能在这里应用呢?序列能够分块,树能不能分块呢?如何分块复杂度才能有保证呢?
i.我们先来看《手把手教你树分块系列》的BZOJ1086 王室联邦。这个题如何解决呢?我们先来陈述一下做法,我们用DFS去分块,我们记录一个栈,当遍历完某个节点的所有子树后,我们将这个节点加入栈,每次遍历完一棵子树,以这个节点为祖先的栈里的元素个数已经超过了我们规定的块的大小B了,就把这些节点变成一个块,将这些元素从栈里弹出,最后将栈中的残留节点归结到最后一个块中。
ii.那么可以确定的是每棵子树对这个节点的贡献不过超过B,为什么呢?因为如果这个子树中的节点数已经超过B了,那么这棵子树本身就会成为一个块。既然我们确定了每棵子树对这个节点的贡献不会超过B,那么我们每遍历完一个子树就检查一次,所以每个块的范围在[B,2B]之间,那么为什么给定的范围强行是[B,3B]呢?因为最后剩余的还要加进最后一个块,这样构造我们可以发现每个块中的节点都是相邻的,这样在块中的两点间距离的上界就是n−−√了
这样复杂度就有保证。
好了,我们会树分块了,莫队算法怎么在树上应用呢?其实是一样的,我们对于一个询问操作记录起始点和终点,每次l -> q[i].l,r -> q[i].r,排序依旧是那样的。
sort(q + 1, q + m + 1, cmp); //对询问进行排序
for(int i = 1; i <= m; i ++){
//什么?这怎么转移啊
while(l != q[i].l) {
//不断的向上跳
//这样简单了啊
}
while(r != q[i].r) {
//不断的向上跳
//这样简单了啊
}
//算出这次询问的答案= =
}
时间复杂度
加上算法a)和算法b),期望得分70。
非常好= =,我们已经拿到70分了。
d) 同样,我们观察第八和第九个测试点,我们可以发现差别在于这里有修改操作了,好!我们如何处理修改操作呢?然后我们发现如果在b)算法中强行安上修改那么时间复杂度会变成O(n2),是不能AC这两个测试点的,那么怎么AC呢?考虑在原来的莫队中加上一维,表示修改的操作时间,如果加上一维,我们块的大小就要变成O(n23)了,排序是前两维按照块的编号排序,最后一维从小到大排序。
我们在转移的时候就变成的O(n23)了,于是转移的时间复杂度就是O(n∗n23)了,具体证明参见b)算法的证明过程。那么我们来算一算第三维转移的时间复杂度,我们发现对于前两维,每一维都有个n13块,于是就有n23种情况,第三维的转移每次都是O(n)的,所以转移的时间复杂度就是O(n∗n23)了,综上所述,总的时间复杂度是O(n∗n23),能通过这两个测试点。
综合以上四个思路,我们可以拿到90分了,算法已经很优秀了。
e) 看到上面四个解法,我们思考一下最后这个点的解法,综合解法c)和解法d)
Vfleaking的解法,按照这个解法,我们发现最后我们只需要暴力的转移即可,我们 发现最后这个式子是我们想要的答案,于是我们将d)的解法转移到树上!我们就 可以拿到100分了!
附上本弱的代码
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 100100
#define LL long long
inline int read(){
char ch = getchar(); 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;
}
struct Edge{
int u, v, next;
} G[M * 2];
int head[M], tot;
int first[M];
int n, m, q;
int v[M], w[M], col[M], cnt[M];
int blo[M];
int st[M], Top;
int height[M], ft[M][20];
int B, cnt_;
bool vis[M];
LL ansnow, ans[M];
struct node{
int x, y, z;
} qr[M];
struct Node{
int x, y, z, id;
LL ans;
inline void sw(){
if(blo[x] > blo[y]) swap(x, y);
}
inline bool operator < (const Node& rhs) const{
if(blo[x] != blo[rhs.x]) return blo[x] < blo[rhs.x];
if(blo[y] != blo[rhs.y]) return blo[y] < blo[rhs.y];
return z < rhs.z;
}
} Q[M];
inline void add(int a, int b);
inline void update(int x);
inline void modify(int x, int y);
inline void moveto(int x, int y);
inline void dfs(int x, int fa, int h);
inline void init();
inline int lca(int a, int b);
inline void getans(int x, int y);
int main(){
n = read(); m = read(); q = read();
memset(head, -1, sizeof(head));
for(int i = 1; i <= m; i ++) v[i] = read();
for(int i = 1; i <= n; i ++) w[i] = read();
for(int i = 1; i < n; i ++){
int a = read(), b = read();
add(a, b); add(b, a);
}
B = pow(n, 2.0/3.0);//确定块的大小
for(int i = 1; i <= n; i ++) col[i] = read(), first[i] = col[i];
//我们用链表记录每个点修改前和修改后的真实颜色
dfs(1, 0, 0); while(Top) blo[st[Top --]] = cnt_; init();
//对树进行分块
int totqr = 0, totQ = 0;
for(int i = 1; i <= q; i ++){
int t = read();
if(t == 0){
qr[++ totqr].x = read();
qr[totqr].y = first[qr[totqr].x];
qr[totqr].z = first[qr[totqr].x] = read();
}
else{
Q[++ totQ] = (Node){read(), read(), totQ, totqr};
Q[totQ].sw(); //一个小小的常数优化
}
}
sort(Q + 1, Q + totQ + 1);
int tim = 1;
while(tim <= Q[1].id){ //转移第三维
modify(qr[tim].x, qr[tim].z);
tim ++;
}
moveto(Q[1].x, Q[1].y);getans(1, lca(Q[1].x, Q[1].y));
for(int i = 2; i <= totQ; i ++){
while(tim <= Q[i].id) modify(qr[tim].x, qr[tim].z),tim ++;
while(tim > Q[i].id + 1) modify(qr[tim - 1].x, qr[tim - 1].y), tim --;
//转移第三维
moveto(Q[i - 1].x, Q[i].x); moveto(Q[i - 1].y, Q[i].y); getans(i, lca(Q[i].x, Q[i].y));
//xor一波 顺便求一下答案
}
for(int i = 1; i <= totQ; i ++) printf("%lld\n", ans[i]);
return 0;
}
inline void add(int a, int b){
G[++ tot] = (Edge){a, b, head[a]};
head[a] = tot;
}
inline void update(int x){
//将一个点从集合中删除
if(vis[x]) ansnow -= (LL)w[cnt[col[x]] --] * v[col[x]];
else ansnow += (LL)w[++ cnt[col[x]]] * v[col[x]];
vis[x] ^= 1;
}
inline void modify(int x, int y){
//修改一个节点的颜色
if(vis[x]) update(x), col[x] = y, update(x);
else col[x] = y;
}
inline void moveto(int x, int y){
//转移,并不需要bfs之类的,两个点直接往上跳
while(x != y){
if(height[x] < height[y]) swap(x, y);
update(x); x = ft[x][0];
}
}
inline void dfs(int x, int fa, int h){
//树分块
int bt = Top;
height[x] = h;
ft[x][0] = fa;
int ot = x;
for(int i = head[x]; i != -1; i = G[i].next) if(G[i].v != fa){
dfs(G[i].v, x, h + 1);
if(Top - bt >= B){
cnt_ ++;
while(Top != bt){
blo[st[Top --]] = cnt_;
}
}
}
st[++ Top] = x;
}
inline void init(){
//预处理倍增数组
for(int i = 1; i <= 18; i ++){
for(int j = 1; j <= n; j ++){
ft[j][i] = ft[ft[j][i - 1]][i - 1];
}
}
}
inline int lca(int a, int b){
//求出lca
if(height[a] < height[b]) swap(a, b);
int t = height[a] - height[b];
for(int i = 18; i >= 0; i --){
if(t & (1 << i)){
a = ft[a][i];
}
}
if(a == b) return a;
for(int i = 18; i >= 0; i --){
if(ft[a][i] != ft[b][i]){
a = ft[a][i];
b = ft[b][i];
}
}
return ft[a][0];
}
inline void getans(int x, int y){
//获取答案,一定要将y这个点去掉,因为y这个点所象征的边不在路径上
update(y);
ans[Q[x].z] = ansnow;
update(y);
}
BZOJ3052——糖果公园的更多相关文章
- 【BZOJ3052】【UOJ#58】【WC2013】糖果公园(树上莫队)
[BZOJ3052][UOJ#58][WC2013]糖果公园(树上莫队) 题面 UOJ 洛谷 Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引 ...
- 【BZOJ3052】[wc2013]糖果公园 带修改的树上莫队
[BZOJ3052][wc2013]糖果公园 Description Input Output Sample Input Sample Input Sample Output 84 131 27 84 ...
- [BZOJ3052][UOJ#58][WC2013]糖果公园
[BZOJ3052][UOJ#58][WC2013]糖果公园 试题描述 Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来 ...
- 【BZOJ-3052】糖果公园 树上带修莫队算法
3052: [wc2013]糖果公园 Time Limit: 200 Sec Memory Limit: 512 MBSubmit: 883 Solved: 419[Submit][Status] ...
- BZOJ3052:[WC2013]糖果公园(树上莫队)
Description Input Output Sample Input 4 3 51 9 27 6 5 12 33 13 41 2 3 21 1 21 4 20 2 11 1 21 4 2 Sam ...
- UOJ #58 【WC2013】 糖果公园
题目链接:糖果公园 听说这是一道树上莫队的入门题,于是我就去写了--顺便复习了一下莫队的各种姿势. 首先,我们要在树上使用莫队,那么就需要像序列一样给树分块.这个分块的过程就是王室联邦这道题(vfle ...
- UOJ58 【WC2013】糖果公园
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- bzoj 3052: [wc2013]糖果公园 带修改莫队
3052: [wc2013]糖果公园 Time Limit: 250 Sec Memory Limit: 512 MBSubmit: 506 Solved: 189[Submit][Status] ...
- 【WC2013】糖果公园
Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园玩. 糖果公园的结构十分奇特,它由 nn 个游览点构成,每个游览 ...
随机推荐
- JavaScript之继承(原型链)
JavaScript之继承(原型链) 我们知道继承是oo语言中不可缺少的一部分,对于JavaScript也是如此.一般的继承有两种方式:其一,接口继承,只继承方法的签名:其二,实现继承,继承实际的方法 ...
- python Scrapy安装和介绍
python Scrapy安装和介绍 Windows7下安装1.执行easy_install Scrapy Centos6.5下安装 1.库文件安装yum install libxslt-devel ...
- 深入JVM-性能监控工具
一.Linux下的性能监控工具 1.1 显式系统整体资源使用情况-top命令 top命令的输出可以分为两个部分:前半部分是系统统计信息,后半部分是进程信息. 在统计信息中,第一行是任务队列信息,他的结 ...
- 签名有元程序集 Signed Friend Assemblies
下面的例子演示了创建签名程序集和有元程序集.这就要求两个程序集都是强命名,在下面的例子中,两个程序集都用了同一个秘钥,也可以用不同的秘钥. 1. 生成秘钥, 这个在前面的博客中有说明,生成秘钥文件sn ...
- Random类
Random类是随机数产生类,可以指定一个随机数的范围,然后任意产生在此范围中的数字. //================================================= // F ...
- aufomaper Queryable Extensions ProjectTo
When using an ORM such as NHibernate or Entity Framework with AutoMapper's standard Mapper.Map funct ...
- RL中“#” “?” &“”号的作用
阅读目录 1. # 2. ? 3. & 回到顶部 1. # 10年9月,twitter改版.一个显著变化,就是URL加入了"#!"符号.比如,改版前的用户主页网址为http ...
- A股博弈笔记
A股博弈笔记 @2014/11/07 A股行情最近甚嚣尘上,似乎是牛市的前奏,指数虽然见涨,但赔钱的股民估计也不少,本人就是其中一个,是我在逆势而行吗? 试图追逐价值投资的方式,而钟情于蓝筹股,本来也 ...
- 新浪微博客户端(59)-hitTest withEvent方法的使用说明
iOS中的触摸事件总是由最顶层的View首先得到的,当这个View得到该触摸事件的时候可以选择通过 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEv ...
- svn三大目录trunk、branch和tag
SVN中Branch和tag的比较在SVN中Branch和tag在一个功能选项中,在使用中也往往产生混淆.在实现上,branch和tag,对于svn都是使用copy实现的,所以他们在默认的权限上和一般 ...