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——糖果公园的更多相关文章

  1. 【BZOJ3052】【UOJ#58】【WC2013】糖果公园(树上莫队)

    [BZOJ3052][UOJ#58][WC2013]糖果公园(树上莫队) 题面 UOJ 洛谷 Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引 ...

  2. 【BZOJ3052】[wc2013]糖果公园 带修改的树上莫队

    [BZOJ3052][wc2013]糖果公园 Description Input Output Sample Input Sample Input Sample Output 84 131 27 84 ...

  3. [BZOJ3052][UOJ#58][WC2013]糖果公园

    [BZOJ3052][UOJ#58][WC2013]糖果公园 试题描述 Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来 ...

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

    3052: [wc2013]糖果公园 Time Limit: 200 Sec  Memory Limit: 512 MBSubmit: 883  Solved: 419[Submit][Status] ...

  5. 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 ...

  6. UOJ #58 【WC2013】 糖果公园

    题目链接:糖果公园 听说这是一道树上莫队的入门题,于是我就去写了--顺便复习了一下莫队的各种姿势. 首先,我们要在树上使用莫队,那么就需要像序列一样给树分块.这个分块的过程就是王室联邦这道题(vfle ...

  7. UOJ58 【WC2013】糖果公园

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

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

    3052: [wc2013]糖果公园 Time Limit: 250 Sec  Memory Limit: 512 MBSubmit: 506  Solved: 189[Submit][Status] ...

  9. 【WC2013】糖果公园

    Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园玩. 糖果公园的结构十分奇特,它由 nn 个游览点构成,每个游览 ...

随机推荐

  1. c语言程序

    汇编语言嵌入到c语言中 #include<stdio.h> int main(void) { int a,b,c; a=4; b=5; _asm { mov eax,a; add eax, ...

  2. WinForm------TreeListLookUpEdit控件的使用

    1.数据库添加表dbo.Graduation 2.从工具栏拖出TreeListLookUpEdit控件,修改部分属性 Display Name:选中后显示在控件的值 Value Member:C#代码 ...

  3. JQuery------$.getJSON()方法的使用

    html(../Home/Index.cshtml) <body> <button class="Btn">按钮</button> </b ...

  4. 第四章 电商云化,4.1 17.5W秒级交易峰值下的混合云弹性架构之路(作者:唐三 乐竹 锐晟 潇谦)

    4.1 17.5W秒级交易峰值下的混合云弹性架构之路 前言 每年的双11都是一个全球狂欢的节日,随着每年交易逐年创造奇迹的背后,按照传统的方式,我们的成本也在逐年上升.双11当天的秒级交易峰值平时的近 ...

  5. 11 Clever Methods of Overfitting and how to avoid them

    11 Clever Methods of Overfitting and how to avoid them Overfitting is the bane of Data Science in th ...

  6. codeforces 724B Batch Sort(暴力-列交换一次每行交换一次)

    题目链接:http://codeforces.com/problemset/problem/724/B 题目大意: 给出N*M矩阵,对于该矩阵有两种操作: (保证,每行输入的数是 1-m 之间的数且不 ...

  7. 快捷键_Mac

    苹果Mac系统常用快捷键 Command+Tab 任意情况下切换应用程序 - 向前循环 Shift+Command+Tab 切换应用程序 - 向后循环 Command+L 当前程序是浏览器时,可以直接 ...

  8. Robot Framework--13 RFS+AutoItLibrary测试web上传下载

    转自:http://blog.csdn.net/tulituqi/article/details/21888059 Selenium2library在我们实际测试web页面的时候基本上已经够用了,不过 ...

  9. xcopy

    xcopy "$(ProjectDir)Admin"  "$(SolutionDir)模块\CanDoo.Admin.WebHost\Admin"  /e/h ...

  10. 网络广告术语CPC、CPM和CTR的含义和关系

    1.       CPC(Cost-per-click):对于广告主来说,就是每次点击(广告)的(付给网站主的)成本:对于媒体(或网站主)来说,就是用户每次点击(广告)(向广告主收取)的费用.可以用公 ...