BZOJ 3757 苹果树 树上莫队

题目大意:给出一棵树,问任意两点之间有多少种不同的颜色,一个人可能会有色盲,会将A和B当成一种颜色。

思路:比较裸的树上莫队,写出来之后,很慢,怀疑是分块的缘故,然后果断找了当年比赛的标称交上去,瞬间rk1,大概看了一眼,他好像是直接用DFS序+曼哈顿距离最小生成树搞的,为什么会比分块快?

昨天下午看到这个题之后就一直在研究树上莫队的正确姿势,然后先写了树分块,后来看了很多牛人的SPOJ COT2的题解,后来又和同学探讨了好久才弄明白。

首先先将树分块,然后把区间排序,按照第一权值为左端点所在块的编号,右端点在DFS序中的位置排序,关键是转移。有一种vfk的靠谱一点的方法。对于任意一个状态,在树上表示[l,r]的路径,目前的状态只存{x|x∈[l,r],x != LCA(l,r)}这些点的颜色,这样就大概有两种情况,一种是两条链,没有中间的LCA,或者是一条链,没有顶端的LCA。然后一直这样转移,例如从[l,r]转移到[x,y]的时候,我们只需要暴力从l->x,y->r,注意记录一个标记数组,在转移的时候把路径上的所有点取反。这样转移之后还是{x|x∈[l,r],x
!= LCA(l,r)}这些点。统计答案的时候将LCA加回来,然后再删掉。

注意:找LCA要倍增!千万别像我以为写不写倍增都是O(n)然后T一晚上。。。

CODE:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 100100
using namespace std;

inline char GetChar()
{
    static const int L = (1 << 15);
    static char buffer[L],*S = buffer,*T = buffer;
    if(S == T) {
        T = (S = buffer) + fread(buffer,1,L,stdin);
        if(S == T)  return EOF;
    }
    return *S++;
}

inline int GetInt()
{
    int c;
    while(!isdigit(c = GetChar()));
    int x = c - '0';
    while(isdigit(c = GetChar()))
        x = (x << 1) + (x << 3) + c - '0';
    return x;
}

int block_size,belong[MAX],blocks;
int pos[MAX],cnt;
int size[MAX],root;

struct Ask{
    int x,y,_id;
    int mixed,_mixed;

    bool operator <(const Ask &a)const {
        if(belong[x] == belong[a.x])    return pos[y] < pos[a.y];
        return belong[x] < belong[a.x];
    }
    void Read(int p) {
        x = GetInt(),y = GetInt();
        mixed = GetInt(),_mixed = GetInt();
        if(pos[x] > pos[y])  swap(x,y);
        _id = p;
    }
}ask[MAX << 1];

int points,asks;
int head[MAX],total;
int _next[MAX << 1],aim[MAX << 1];

int deep[MAX],father[MAX][20];

int src[MAX];

int num[MAX],colors;
bool v[MAX];
int ans[MAX];

inline void Add(int x,int y)
{
    _next[++total] = head[x];
    aim[total] = y;
    head[x] = total;
}

void DFS(int x,int last)
{
    father[x][0] = last;
    pos[x] = ++cnt;
    deep[x] = deep[last] + 1;
    for(int i = head[x]; i; i = _next[i]) {
        if(aim[i] == last)  continue;
        if(size[belong[x]] < block_size)
            ++size[belong[x]],belong[aim[i]] = belong[x];
        else    ++size[++blocks],belong[aim[i]] = blocks;
        DFS(aim[i],x);
    }
}

inline void Change(int x,int c)
{
    if(!num[x]) ++num[x],++colors;
    else if(num[x] == 1 && c == -1) --num[x],--colors;
    else    num[x] += c;
}

inline int GetLCA(int x,int y)
{
    if(deep[x] < deep[y])    swap(x,y);
    for(int i = 19; ~i; --i)
        if(deep[father[x][i]] >= deep[y])
            x = father[x][i];
    if(x == y)  return x;
    for(int i = 19; ~i; --i)
        if(father[x][i] != father[y][i])
            x = father[x][i],y = father[y][i];
    return father[x][0];
}

inline void Work(int x,int y,int lca)
{
    for(; x != lca; x = father[x][0]) {
        Change(src[x],v[x] ? -1:1);
        v[x] ^= 1;
    }
    for(; y != lca; y = father[y][0]) {
        Change(src[y],v[y] ? -1:1);
        v[y] ^= 1;
    }
}

inline void Solve(int p)
{
    static int l = root,r = root,lca;

    Work(l,ask[p].x,GetLCA(l,ask[p].x));
    Work(r,ask[p].y,GetLCA(r,ask[p].y));
    l = ask[p].x,r = ask[p].y;
    lca = GetLCA(l,r);
    Change(src[lca],1);
    ans[ask[p]._id] = colors;
    if(ask[p].mixed != ask[p]._mixed)
        ans[ask[p]._id] -= num[ask[p].mixed] && num[ask[p]._mixed];
    Change(src[lca],-1);
}

inline void SparseTable()
{
    for(int j = 1; j < 20; ++j)
        for(int i = 1; i <= points; ++i)
            father[i][j] = father[father[i][j - 1]][j - 1];
}

int main()
{
    //freopen("apple.in","r",stdin);
    //freopen("apple.out","w",stdout);
    cin >> points >> asks;
    for(int i = 1; i <= points; ++i)
        src[i] = GetInt();
    for(int x,y,i = 1; i <= points; ++i) {
        x = GetInt(),y = GetInt();
        Add(x,y),Add(y,x);
    }
    block_size = sqrt(points + 1);
    size[1] = 1;
    belong[0] = 1;
    blocks = 1;
    DFS(0,MAX - 1);
    root = aim[head[0]];
    SparseTable();

    for(int i = 1; i <= asks; ++i)
        ask[i].Read(i);
    sort(ask + 1,ask + asks + 1);

    for(int i = 1; i <= asks; ++i)
        Solve(i);
    for(int i = 1; i <= asks; ++i)
        printf("%d\n",ans[i]);
    return 0;
}
时间: 2024-10-09 09:22:31

BZOJ 3757 苹果树 树上莫队的相关文章

【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)

学习了树上莫队,树分块后对讯问的$dfs序$排序,然后就可以滑动树链处理答案了. 关于树链的滑动,只需要特殊处理一下$LCA$就行了. 在这里一条树链保留下来给后面的链来转移的$now$的为这条树链上所有点除去$LCA$的颜色种数.因为如果要考虑$LCA$情况就太多了,不如单独考虑$LCA$. 转移后加上当前链的$LCA$进行统计,然后再去掉这个$LCA$更新一下$now$值给后面的链转移. 这都是我的理解,说的有点不清楚,具体请看vfk的题解 OTZ 虽然不是这道题,但是通过这篇博客学习树上莫

bzoj 3757: 苹果树(树上莫队)

3757: 苹果树 Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 1327  Solved: 510 [Submit][Status][Discuss] Description 神犇家门口种了一棵苹果树.苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条.由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色.我们用一个1到n之间的正整数来表示一种颜色

【BZOJ-3757】苹果树 块状树 + 树上莫队

3757: 苹果树 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1305  Solved: 503[Submit][Status][Discuss] Description 神犇家门口种了一棵苹果树.苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条.由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色.我们用一个到n之间的正整数来表示一种颜色.树上

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

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

(树上莫队)HDU - 5799 This world need more Zhu

题意: 两种询问: 1.询问以u为根的子树中出现的a次的数的和与出现b次的数的和的gcd. 2.询问u到v的树链中出现的a次的数的和与出现b次的数的和的gcd. 有点绕.. 分析: 因为自己不会树上莫队,所以学习了一波. 但是对于子树我还是有所经验,可以转成dfs序来做,之前有做过类似的题,比如这题. 然而对于树链有点懵逼,虽然我觉得也能用dfs序做,不过看大佬们的dfs序做的树链查询,也有点懵,感觉写起来很麻烦. 貌似是修改了dfs序,回溯的时候不再只是和进入时相同的序,而是独立的序. 还是感

[spoj COT2]树上莫队

题目链接:http://www.spoj.com/problems/COT2/ 学会了树上莫队,真的是太激动了!参照博客:http://codeforces.com/blog/entry/43230 讲的十分清楚. #include<bits/stdc++.h> using namespace std; const int MAXN=40005; const int maxm=100005; int cur; int sat[MAXN]; int ean[MAXN]; int A[MAXN*2

SPOJ COT2 Count on a tree II (树上莫队,倍增算法求LCA)

题意:给一个树图,每个点的点权(比如颜色编号),m个询问,每个询问是一个区间[a,b],图中两点之间唯一路径上有多少个不同点权(即多少种颜色).n<40000,m<100000. 思路:无意中看到树上莫队,只是拿来练练,没有想到这题的难点不在于树上莫队,而是判断LCA是否在两点之间的路径上的问题.耗时1天. 树上莫队的搞法就是: (1)DFS一次,对树进行分块,分成sqrt(n)块,每个点属于一个块.并记录每个点的DFS序. (2)将m个询问区间用所属块号作为第一关键字,DFS序作为第二关键字

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这个点,特殊处理一下即可,然后

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

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