zoukankan      html  css  js  c++  java
  • 染色「SDOI 2011」

    【题目描述】
    给定一棵有n个节点的无根树和m个操作,操作有2类:
    1、将节点a到节点b路径上所有点都染成颜色c;
    2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。

    请你写一个程序依次完成这m个操作。

    【输入格式】
    第一行包含2个整数n和m,分别表示节点数和操作数;
    第二行包含n个正整数表示n个节点的初始颜色
    下面n-1行每行包含两个整数x和y,表示x和y之间有一条无向边。
    下面m行每行描述一个操作:
    “C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
    “Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

    【输出格式】
    对于每个询问操作,输出一行答案。



    题解
    看到这种树上操作路径的题大概率就是树链剖分了
    先考虑一下 如果是一条链要怎么做
    显然 可以用线段树维护 每个区间记录三个值:此区间内的颜色段数量,区间最左边点的颜色,区间最右边点的颜色
    那么 一个区间的颜色段数量=左区间颜色段数量+右区间颜色段数量-(左区间最右颜色==右区间最左颜色);最左边点颜色=左区间最左点颜色 最右边点颜色=右区间最右边点颜色
    查询也是差不多 把左儿子返回的结果(这里可以返回一个结构体)和右儿子返回的结果按上一行的方法合并一下即可

    然而树剖查询一条路径时是有最多log次的线段树区间查询 而不是直接查询一个区间 怎么合并这些查询答案统计出最终答案呢
    其实只需要记录一下上一次查询区间的左端点颜色 如果和这一次查询区间的右端点颜色相同 答案-1即可
    具体可见代码 统计答案这段还是有点抽搐

    ps: 这题线段树区间修改要打懒标记

    【代码】

    #include <bits/stdc++.h>
    using namespace std;
    
    int n, m, a[100005], b1, b2, b3, lst[10];
    int head[100005], pre[200005], to[200005], sz;
    int fa[100005], d[100005], st[100005], dfn[100005], son[100005], pos[100005], siz[100005], tme;
    char tp[5];
    struct segtree{
    	int l, r, lcol, rcol, cnt, tag;
    	segtree() {
    		lcol = rcol = tag = -1; l = r = cnt = 0;
    	}
    } tr[400005];
    
    inline void addedge(int u, int v) {
    	pre[++sz] = head[u]; head[u] = sz; to[sz] = v;
    }
    
    namespace Segtree{
    	inline void pushup(int ind) {
    		tr[ind].cnt = tr[ind<<1].cnt + tr[ind<<1|1].cnt - (tr[ind<<1].rcol == tr[ind<<1|1].lcol);
    		tr[ind].lcol = tr[ind<<1].lcol; tr[ind].rcol = tr[ind<<1|1].rcol;
    	}
    	
    	inline void pushdown(int ind) {
    		if (tr[ind].tag == -1) return;
    		tr[ind<<1].cnt = tr[ind<<1|1].cnt = 1; 
    		tr[ind<<1].lcol = tr[ind<<1|1].lcol = tr[ind<<1].rcol = tr[ind<<1|1].rcol = tr[ind].tag;
    		tr[ind<<1].tag = tr[ind<<1|1].tag = tr[ind].tag; tr[ind].tag = -1;
    	}
    	
    	void build(int ind, int l, int r) {
    		tr[ind] = segtree();
    		tr[ind].l = l; tr[ind].r = r; tr[ind].tag = -1;
    		if (l == r) {
    			tr[ind].cnt = 1; tr[ind].lcol = tr[ind].rcol = a[pos[l]];
    			return;
    		}
    		int mid = (l + r) >> 1;
    		build(ind<<1, l, mid); build(ind<<1|1, mid+1, r);
    		pushup(ind);
    	}
    	
    	void update(int ind, int x, int y, int v) {
    		int l = tr[ind].l, r = tr[ind].r;
    		if (x <= l && r <= y) {
    			tr[ind].cnt = 1; tr[ind].lcol = tr[ind].rcol = v; tr[ind].tag = v; return;
    		}
    		int mid = (l + r) >> 1;
    		pushdown(ind);
    		if (x <= mid) update(ind<<1, x, y, v);
    		if (mid < y) update(ind<<1|1, x, y, v);
    		pushup(ind);
    	}
    	
    	inline segtree merge(segtree a, segtree b) {
    		segtree ret = segtree();
    		ret.cnt = a.cnt + b.cnt - (a.rcol == b.lcol);
    		ret.lcol = a.lcol; ret.rcol = b.rcol;
    		return ret;
    	}
    	
    	segtree query(int ind, int x, int y) {
    		int l = tr[ind].l, r = tr[ind].r;
    		if (x <= l && r <= y) {
    			return tr[ind];
    		}
    		int mid = (l + r) >> 1;
    		pushdown(ind);
    		segtree ret1 = segtree(), ret2 = segtree(); 
    		if (x <= mid) ret1 = query(ind<<1, x, y);
    		if (mid < y) ret2 = query(ind<<1|1, x, y);
    		if (!ret1.cnt) return ret2;
    		else if (!ret2.cnt) return ret1;
    		else return merge(ret1, ret2);
    	}
    }
    
    using namespace Segtree;
    
    namespace treechains{
    	void dfs1(int x, int f) {
    		siz[x] = 1;
    		for (int i = head[x]; i; i = pre[i]) {
    			int y = to[i];
    			if (y == f) continue;
    			d[y] = d[x] + 1; fa[y] = x; 
    			dfs1(y, x); siz[x] += siz[y];
    			if (siz[y] > siz[son[x]]) son[x] = y;
    		}
    	}
    	
    	void dfs2(int x, int start) {
    		dfn[x] = ++tme; pos[tme] = x; st[x] = start;
    		if (son[x]) dfs2(son[x], start);
    		for (int i = head[x]; i; i = pre[i]) {
    			int y = to[i];
    			if (y != fa[x] && y != son[x]) dfs2(y, y);
    		}
    	}
    	
    	void change(int x, int y, int z) {
    		while (st[x] != st[y]) {
    			if (d[st[x]] < d[st[y]]) swap(x, y);
    			update(1, dfn[st[x]], dfn[x], z);
    			x = fa[st[x]];			
    		}
    		if (dfn[x] > dfn[y]) swap(x, y);
    		update(1, dfn[x], dfn[y], z);
    	}
    	
    	int ask(int x, int y) {
    		int ret = 0, o = 0; lst[0] = lst[1] = -1;
                    //lst[0]: x~lca这条链上上一次查询的左端点颜色; lst[1]: y~lca这条链上上一次查询的左端点颜色
    		segtree tmp = segtree();
    		while (st[x] != st[y]) {
    			if (d[st[x]] < d[st[y]]) swap(x, y), o ^= 1;
    			tmp = query(1, dfn[st[x]], dfn[x]);
    			ret += tmp.cnt - (tmp.rcol == lst[o]); lst[o] = tmp.lcol;
    			x = fa[st[x]];			
    		}
    		if (dfn[x] > dfn[y]) swap(x, y), o ^= 1;
    		tmp = query(1, dfn[x], dfn[y]);
    		if (!o) {
    			ret += tmp.cnt - (lst[0] == tmp.lcol) - (lst[1] == tmp.rcol);
    		} else {
    			ret += tmp.cnt - (lst[1] == tmp.lcol) - (lst[0] == tmp.rcol);
    		}
    		return ret;
    	}
    }
    
    using namespace treechains;
    
    int main() {
    	scanf("%d %d", &n, &m);
    	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    	for (int i = 1; i < n; i++) {
    		int x, y; scanf("%d %d", &x, &y);
    		addedge(x, y); addedge(y, x);
    	}
    	dfs1(1, 0); dfs2(1, 1);
    	build(1, 1, n);
    	for (int i = 1; i <= m; i++) {
    		scanf("%s", tp);
    		if (tp[0] == 'C') {
    			scanf("%d %d %d", &b1, &b2, &b3);
    			change(b1, b2, b3);
    		} else {
    			scanf("%d %d", &b1, &b2);
    			printf("%d
    ", ask(b1, b2));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    [转]天龙八部的BillingServer
    [转]天龙八部服务器端Lua脚本系统
    [转]天龙八部服务器端-共享内存的设计
    TCPSocket v1.0 for cocos2d-x下载
    httpclient模拟浏览器getpost
    常用的32中算法
    我在城市快节奏中的慢生活
    分段与分页机制小结
    lua中dofile、loadfile、require区别
    两个链表相交以及第一个公共节点的问题
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM26.html
Copyright © 2011-2022 走看看