CF375D Tree and Querie 题解

本文详细介绍了如何使用线段树处理图数据结构中的问题,特别是针对颜色计数和出现次数的统计。通过线段树合并技巧,实现了在O(n)时间复杂度内更新和查询颜色信息。同时,提出了先合并可合并节点再暴力修改的策略,以优化合并过程。代码示例展示了在C++中的实现,并在实际问题中应用线段树进行高效求解。

CF375D Tree and Queries

线段树合并。

需要记录两颗线段树,其中一棵表示当前节点的颜色数量,另外一个表示出现次数为 i i i 的颜色个数。

前面一棵直接进行线段树合并即可。后面的那棵能线段树合并的条件当且仅当有一种颜色只在一个子树中出现,我们可以直接对于这种情况进行合并。不然我们需要修改颜色,也就是暴力修改。

暴力修改的次数最多是 O ( n ) O(n) O(n) 次。复杂度正确。

T r i c k s : Tricks: Tricks

我们先将可以进行合并的节点进行合并,之后再暴力修改。具体来说就是先统计哪个颜色是不符合标准的,之后将包含这个颜色的节点的所有贡献减去。之后直接线段树合并即可。

我们可以在线段树合并到叶子节点的时候记录同一个颜色在几个子树中出现过。

注意: 在线段树合并结束的时候不要忘记将权值合并。

#include <bits/stdc++.h>
using namespace std;

//#define Fread
//#define Getmod

#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#define getchar gc
#endif // Fread

template <typename T>
void r1(T &x) {
	x = 0;
	char c(getchar());
	int f(1);
	for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
	x *= f;
}

#ifdef Getmod
const int mod  = 1e9 + 7;
template <int mod>
struct typemod {
    int z;
    typemod(int a = 0) : z(a) {}
    inline int inc(int a,int b) const {return a += b - mod, a + ((a >> 31) & mod);}
    inline int dec(int a,int b) const {return a -= b, a + ((a >> 31) & mod);}
    inline int mul(int a,int b) const {return 1ll * a * b % mod;}
    typemod<mod> operator + (const typemod<mod> &x) const {return typemod(inc(z, x.z));}
    typemod<mod> operator - (const typemod<mod> &x) const {return typemod(dec(z, x.z));}
    typemod<mod> operator * (const typemod<mod> &x) const {return typemod(mul(z, x.z));}
    typemod<mod>& operator += (const typemod<mod> &x) {*this = *this + x; return *this;}
    typemod<mod>& operator -= (const typemod<mod> &x) {*this = *this - x; return *this;}
    typemod<mod>& operator *= (const typemod<mod> &x) {*this = *this * x; return *this;}
    int operator == (const typemod<mod> &x) const {return x.z == z;}
    int operator != (const typemod<mod> &x) const {return x.z != z;}
};
typedef typemod<mod> Tm;
#endif

template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
    r1(t);  r1(args...);
}

//#define int long long
const int maxn = 2e5 + 5;
const int maxm = maxn << 1;

int head[maxn], cnt;
struct Edge {
    int to, next;
}edg[maxn << 1];
void add(int u,int v) {
    edg[++ cnt] = (Edge) {v, head[u]}, head[u] = cnt;
}

struct Node {
    int l, r, val;
}t[maxn * 40];

int tot(0);
int tag[maxn];
vector<int> V, C;
const int N = 1e5;
struct Seg {
    #define ls t[p].l
    #define rs t[p].r
    #define mid ((l + r) >> 1)
    void Insert(int &p,int l,int r,int pos,int c) {
        if(!p) p = ++ tot;
        t[p].val += c;
        if(l == r) return ;
        if(pos <= mid) Insert(ls, l, mid, pos, c);
        else Insert(rs, mid + 1, r, pos, c);
    }
    int merge(int u,int v,int l,int r) {
        if(!u || !v) return u + v;
        if(l == r) {
            if(!tag[l] && t[u].val > 0) {
                tag[l] = 1;
                C.push_back(l);
                V.push_back(t[u].val);
            }
            if(t[v].val > 0) V.push_back(t[v].val);
            t[u].val += t[v].val;
            return u;
        }
        t[u].val += t[v].val;
        t[u].l = merge(t[u].l, t[v].l, l, mid);
        t[u].r = merge(t[u].r, t[v].r, mid + 1, r);
        return u;
    }
    int ask(int p,int l,int r,int ll,int rr) {
        if(!p) return 0;
        if(ll <= l && r <= rr) return t[p].val;
        int res(0);
        if(ll <= mid) res += ask(ls, l, mid, ll, rr);
        if(mid < rr) res += ask(rs, mid + 1, r, ll, rr);
        return res;
    }
}T;

int rt1[maxn], rt2[maxn];

struct Query {
    int k, id;
};

int ans[maxn], a[maxn];
vector<Query> v[maxn];

void dfs(int p,int pre) {
    for(int i = head[p];i;i = edg[i].next) {
        int to = edg[i].to; if(to == pre) continue;
        dfs(to, p);
    }
    V.clear(), C.clear();
    T.Insert(rt1[p], 1, N, a[p], 1);
    T.Insert(rt2[p], 1, N, 1, 1);
    for(int i = head[p];i;i = edg[i].next) {
        int to = edg[i].to; if(to == pre) continue;
        rt1[p] = T.merge(rt1[p], rt1[to], 1, N);
    }
    for(int i = 0; i < V.size(); ++ i) T.Insert(rt2[p], 1, N, V[i], -1); // 减去多余线段树合并的贡献
    for(int i = 0; i < C.size(); ++ i) T.Insert(rt2[p], 1, N, T.ask(rt1[p], 1, N, C[i], C[i]), 1); // 统计所有颜色
    for(int i = head[p];i;i = edg[i].next) {
        int to = edg[i].to; if(to == pre) continue;
        rt2[p] = T.merge(rt2[p], rt2[to], 1, N);
    }
    for(auto x : v[p]) ans[x.id] = T.ask(rt2[p], 1, N, x.k, N);
//    printf("valall = %d\n", T.ask(rt2[p], 1, N, 1, N));
    for(int i = 0; i < C.size(); ++ i) tag[C[i]] = 0;
}

int n, m;
/*
4 1
1 2 3 4
1 2
2 3
3 4
1 1
*/
signed main() {
//    freopen("S.in", "r", stdin);
//    freopen("S.out", "w", stdout);
    int i, j;
    r1(n, m);
    for(i = 1; i <= n; ++ i) r1(a[i]);
    for(i = 1; i < n; ++ i) {
        int u, v;
        r1(u, v);
        add(u, v), add(v, u);
    }
    for(i = 1; i <= m; ++ i) {
        int u, k;
        r1(u, k);
        v[u].push_back((Query) {k, i});
    }
    dfs(1, 0);
    for(i = 1; i <= m; ++ i) printf("%d\n", ans[i]);
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值