BZOJ3720 Gty的妹子树Solution

题目大意:

维护一棵初始有n个节点的有根树(根节点为1),树上节点编号为1-n,每个点有一个权值wi。


支持以下操作:

0 u x          询问以u为根的子树中,严格大于x的值的个数。(u^=lastans,x^=lastans)

1 u x          把u节点的权值改成x。(u^=lastans,x^=lastans)

2 u x          添加一个编号为"当前树中节点数+1"的节点,其父节点为u,其权值为x。(u^=lastans,x^=lastans)

最开始时lastans=0。

思路:开始看到询问是与子树有关的,又要支持动态加点,想到动态维护dfs序,然而一个区间第k大就让splay滚粗了。

然后离线构树也不行。

那么觉得不可做,想到分块乱搞。

然而这是一棵树,如何进行树上分块呢?


我们用一次dfs过程将树分块。

首先,定义根节点在块1中,块1的size为1.

从根节点向下dfs,对于当前点x,依次遍历其儿子,若遍历到儿子son时x所在的块的size小于Maxsize,则将son加入x所在的块,x所在的块的size+1.

否则,建立一个新块,使son加入这个新块,新块的size+1.

每遍历到一个儿子就向下dfs.


然后我们记录两个图,一个是原来的树,一个是块与块之间形成的图,只需要在每次产生新块的时候加一条边即可。

不难发现,块与块之间事实上形成的也是树形结构。

我们再对于每一个块记录下块内所有的权值,并排序。


对于询问,我们从当前节点向下dfs,若遇到一个儿子不属于其所在的块,则转到块与块之间形成的树向下搜索,每遇到一个块就在块里二分找答案。如果儿子依旧属于这个块,那就直接看一下他和x的大小关系即可。若令Maxsize=sqrt(n),这样的复杂度是O(sqrt(n)log(sqrt(n))).

对于加入新点,我们直接在其父亲所在的块中加入一个点即可,需要暴力修改权值的有序序列,复杂度O(sqrt(n)).当然如果父亲所在的块size达到了MAxsize,就新建一个一个点的新块。

如果修改权值,就直接在这个点所在的块的权值序列中暴力删除,插入即可,时间复杂度O(sqrt(n)).


事实上可能取Maxsize=sqrt(n)logn快一些?不过我T了。。。


Code:

#include <cmath>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
 
inline int getc() {
    static const int L = 1 << 15;
    static char buf[L], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, L, stdin);
        if (S == T)
            return EOF;
    }
    return *S++;
}
inline int getint() {
    int c;
    while(!isdigit(c = getc()));
    int tmp = c - '0';
    while(isdigit(c = getc()))
        tmp = (tmp << 1) + (tmp << 3) + c - '0';
    return tmp;
}
 
#define N 30010
#define M 30010
#define Num 5010
#define Size 5010
 
struct Graph {
    int head[N + M], next[(N + M)<<1], end[(N + M)<<1], ind;
    void reset() {
        ind = 0;
        memset(head, -1, sizeof(head));
    }
    void addedge(int a, int b) {
        int q = ind++;
        end[q] = b;
        next[q] = head[a];
        head[a] = q;
    }
    void make(int a, int b) {
        addedge(a, b);
        addedge(b, a);
    }
}G;
 
struct Lump {
    int head[Num], next[Num << 1], end[Num << 1], ind;
    void reset() {
        ind = 0;
        memset(head, -1, sizeof(head));
    }
    void addedge(int a, int b) {
        int q = ind++;
        end[q] = b;
        next[q] = head[a];
        head[a] = q;
    }
    void make(int a, int b) {
        addedge(a, b);
        addedge(b, a);
    }
}Lumps;
 
int w[N + M];
 
int per, size[Num], sav[Num][Size], belong[N + M], cnt, pa[N + M];
void dfs(int x, int fa) {
    for(int j = G.head[x]; j != -1; j = G.next[j]) {
        if (G.end[j] == fa)
            continue;
        pa[G.end[j]] = x;
        if (size[belong[x]] == per) {
            belong[G.end[j]] = ++cnt, sav[cnt][++size[cnt]] = w[G.end[j]];
            Lumps.make(cnt, belong[x]);
        }
        else
            belong[G.end[j]] = belong[x], sav[belong[x]][++size[belong[x]]] = w[G.end[j]];
        dfs(G.end[j], x);
    }
}
 
inline int ask_Big(int ins, int x) {
    sav[ins][size[ins] + 1] = 1 << 30;
    int L = 1, R = size[ins] + 1, mid;
    while(L < R) {
        mid = (L + R) >> 1;
        if (sav[ins][mid] > x)
            R = mid;
        else
            L = mid + 1;
    }
    return size[ins] + 1 - L;
}
inline int ask_Lump(int x, int fa, int val) {
    int res = ask_Big(x, val);
    for(int j = Lumps.head[x]; j != -1; j = Lumps.next[j]) {
        if (Lumps.end[j] != fa)
            res += ask_Lump(Lumps.end[j], x, val);
    }
    return res;
}
inline int ask(int x, int fa, int val) {
    int res = w[x] > val ? 1 : 0;
    for(int j = G.head[x]; j != -1; j = G.next[j]) {
        if (G.end[j] != fa) {
            if (belong[G.end[j]] != belong[x])
                res += ask_Lump(belong[G.end[j]], belong[x], val);
            else
                res += ask(G.end[j], x, val);
        }
    }
    return res;
}
 
void remove(int ins, int val) {
    int i, j;
    for(i = 1; i <= size[ins]; ++i)
        if (sav[ins][i] == val)
            break;
    --size[ins];
    for(j = i; j <= size[ins]; ++j)
        sav[ins][j] = sav[ins][j + 1];
}
void insert(int ins, int val) {
    int i, j;
    for(i = 1; i <= size[ins]; ++i)
        if (sav[ins][i] > val)
            break;
    ++size[ins];
    for(j = size[ins]; j > i; --j)
        sav[ins][j] = sav[ins][j - 1];
    sav[ins][i] = val;
}
 
int main() {
    int n = getint();
     
    register int i;
    int a, b;
    G.reset();
    for(i = 1; i < n; ++i) {
        a = getint(), b = getint();
        G.make(a, b);
    }
     
    for(i = 1; i <= n; ++i)
        w[i] = getint();
     
    per = (int)sqrt(n * log(n)/log(2));
     
    Lumps.reset();
    belong[1] = size[++cnt] = 1, sav[cnt][1] = w[1];
    dfs(1, -1);
     
    for(i = 1; i <= cnt; ++i)
        sort(sav[i] + 1, sav[i] + size[i] + 1);
     
    int Q, ope, lastans = 0;
    Q = getint();
    while(Q--) {
        ope = getint(), a = getint(), b = getint();
        a ^= lastans;
        b ^= lastans;
        if (!ope)
            printf("%d\n", lastans = ask(a, pa[a], b));
        else if (ope == 1) {
            remove(belong[a], w[a]);
            insert(belong[a], w[a] = b);
        }
        else {
            w[++n] = b;
            pa[n] = a;
            if (size[belong[a]] == per) {
                belong[n] = ++cnt;
                size[cnt] = 1;
                sav[cnt][1] = w[n];
                G.make(a, n);
                Lumps.make(belong[a], cnt);
            }
            else {
                belong[n] = belong[a];
                insert(belong[a], w[n]);
                G.make(a, n);
            }
        }
    }
     
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值