zoukankan      html  css  js  c++  java
  • P2486 [SDOI2011]染色

    题意描述:

    洛谷

    给你一颗 (n) 个节点的树,有 (m) 次操作,每次操作分为两种:

    • (x-y) 路径上的点赋成颜色 (c)
    • 询问 (x-y) 的路径上的点颜色段的数量

    数据范围: (n,mleq 10^5)

    solution

    树剖的经典题。

    看到这个题的第一眼就会想到树剖,但我们想一下这个怎么维护答案。

    我们对于线段树上每个点维护三个信息:最左边的颜色,最右边的颜色,颜色段的数量。

    假设我现在想推出节点 ( o) 的颜色段的数量。

    那么显然是可以由 (lc(o),rc(o)) 这两段拼接起来的。

    然后在线段树中就是表示为: (sum(o) = sum(o<<1) + sum(o<<1|1))

    但是如果左儿子最右边的颜色等于右儿子最左边的颜色相等的话,显然中间有两段是可以合并在一起的,所以 (sum(o) -= 1)

    void up(int o)
    {
    	sum(o) = sum(o<<1) + sum(o<<1|1);
    	lc(o) = lc(o<<1);
    	rc(o) = rc(o<<1|1);
    	if(rc(o<<1) == lc(o<<1|1)) sum(o)--; 
    }
    

    考虑得到操作 (2) 的答案,我们在进行树剖的时候会把 (x-y) 的路径分成若干条重链,且重链之间由轻边连接。

    对于一条重链,我们在线段树上查询一下就可以得到这一段的答案,因此让总答案加上这一部分即可。

    同时对于两条重链相接的地方也就是轻边 (top[x] 和 fa[top[x]]) ,如果这两个点的颜色是相同的话,那么中间有两段肯定是可以合并在一起的,所以总答案要减一。

    剩下的就是线段树套数剖的区间覆盖操作,随便维护一下就好了。

    code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 1e5+10; 
    int n,m,tot,cnt,num,u,v,x,y,c;
    int siz[N],dfn[N],dep[N],fa[N],top[N],head[N],w[N],a[N],son[N];
    struct node
    {
    	int to,net;
    }e[N<<1];
    struct Tree
    {
    	int lc,rc,sum,tag;
    }tr[N<<2];
    #define lc(o) tr[o].lc
    #define rc(o) tr[o].rc
    #define sum(o) tr[o].sum
    #define tag(o) tr[o].tag
    inline int read()
    {
        int s = 0, w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    void add(int x,int y)
    {
    	e[++tot].to = y;
    	e[tot].net = head[x];
    	head[x] = tot;
    }
    void get_tree(int x)
    {
    	dep[x] = dep[fa[x]] + 1; siz[x] = 1;
    	for(int i = head[x]; i; i = e[i].net)
    	{
    		int to = e[i].to;
    		if(to == fa[x]) continue;
    		fa[to] = x;
    		get_tree(to);
    		siz[x] += siz[to];
    		if(siz[to] > siz[son[x]]) son[x] = to; 
    	} 
    }
    void dfs(int x,int topp)
    {
    	top[x] = topp; dfn[x] = ++num; w[dfn[x]] = a[x];
    	if(son[x]) dfs(son[x],topp);
    	for(int i = head[x]; i; i = e[i].net)
    	{
    		int to = e[i].to;
    		if(to == fa[x] || to == son[x]) continue;
    		dfs(to,to);
    	} 
    }
    void up(int o)//维护一下这个点的信息
    {
    	sum(o) = sum(o<<1) + sum(o<<1|1);
    	lc(o) = lc(o<<1);
    	rc(o) = rc(o<<1|1);
    	if(rc(o<<1) == lc(o<<1|1)) sum(o)--; 
    }
    void cover(int o,int val)
    {
    	sum(o) = 1;
    	lc(o) = rc(o) = val;
    	tag(o) = val;
    }
    void down(int o)
    {
    	if(tag(o))
    	{
    		cover(o<<1,tag(o));
    		cover(o<<1|1,tag(o));
    		tag(o) = 0;
    	}
    }
    void build(int o,int l,int r)
    {
    	if(l == r)
    	{
    		sum(o) = 1;
    		lc(o) = rc(o) = w[l];
    		return;
    	}
    	int mid = (l+r)>>1;
    	build(o<<1,l,mid);
    	build(o<<1|1,mid+1,r);
    	up(o);
    }
    void chenge(int o,int l,int r,int L,int R,int val)
    {
    	if(L <= l && R >= r)
    	{
    		cover(o,val);
    		return;
    	}
    	down(o);
    	int mid = (l+r)>>1;
    	if(L <= mid) chenge(o<<1,l,mid,L,R,val);
    	if(R > mid) chenge(o<<1|1,mid+1,r,L,R,val);
    	up(o);
    }
    int query(int o,int l,int r,int L,int R)
    {
    	if(L <= l && R >= r) return sum(o);
    	down(o);
    	int mid = (l+r)>>1;
    	if(R <= mid) return query(o<<1,l,mid,L,R);
    	else if(L > mid) return query(o<<1|1,mid+1,r,L,R);
    	else return query(o<<1,l,mid,L,R) + query(o<<1|1,mid+1,r,L,R) + (rc(o<<1) == lc(o<<1|1) ? -1 :0);
    }
    int ask(int o,int l,int r,int x)
    {
    	if(l == r) return lc(o);
    	down(o);
    	int mid = (l+r)>>1;
    	if(x <= mid) return ask(o<<1,l,mid,x);
    	if(x > mid) return ask(o<<1|1,mid+1,r,x);
    }
    void modify(int x,int y,int val)
    {
    	while(top[x] != top[y])
    	{
    		if(dep[top[x]] < dep[top[y]]) swap(x,y);
    		chenge(1,1,n,dfn[top[x]],dfn[x],val);
    		x = fa[top[x]];
    	} 
    	if(dep[x] >= dep[y]) swap(x,y);
    	chenge(1,1,n,dfn[x],dfn[y],val);
    }
    int get(int x,int y)//得到答案
    {
    	int res = 0;
    	while(top[x] != top[y])
    	{
    		if(dep[top[x]] < dep[top[y]]) swap(x,y);
    		res += query(1,1,n,dfn[top[x]],dfn[x]);
    		int a = ask(1,1,n,dfn[top[x]]);
    		int b = ask(1,1,n,dfn[fa[top[x]]]);
    		if(a == b) res--;
    		x = fa[top[x]]; 
    	}
    	if(dep[x] >= dep[y]) swap(x,y);
    	res += query(1,1,n,dfn[x],dfn[y]);
    	return res;
    }
    int main()
    {
    	n = read(); m = read();
    	for(int i = 1; i <= n; i++) a[i] = read();
    	for(int i = 1; i <= n-1; i++)
    	{
    		u = read(); v = read();
    		add(u,v); add(v,u);
    	} 
    	get_tree(1); dfs(1,1); build(1,1,n);
    	for(int i = 1; i <= m; i++)
    	{
    		char ch; cin>>ch; x = read(); y = read();
    		if(ch == 'C') c = read(), modify(x,y,c);
    		if(ch == 'Q') printf("%d
    ",get(x,y));
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    在你设计中可能用到的20个杂志 PSD 原型
    Gradify
    CamanJS – 提供各种图片处理的 JavaScript 库
    免费素材:包含 250+ 组件的 DO UI Kit
    24个很赞的 Node.js 免费教程和在线指南
    Dynamics.js
    Page Scroll Effects
    Slides
    15款加速 Web 开发的 JavaScript 框架
    Wee – 为现代 Web 开发打造的 CSS 脚手架
  • 原文地址:https://www.cnblogs.com/genshy/p/14427357.html
Copyright © 2011-2022 走看看