HDU 5893 List wants to travel

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5893


List wants to travel

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)

Total Submission(s): 603    Accepted Submission(s): 154



Problem Description

A boy named List who is perfect in English. Now he wants to travel and he is making a plan. But the cost of living in same citie always changes. Now he wants to know how many different kinds of continuous same cost he has to pay for living between two cities. Can you help him? (He is so lazy to do this by himself.)
 

Input

There are multiple cases. The first line contains two positive numbers N and M(N (N<=40000) where N is the amount of cities and M (M<=50000)) is the amount of operations.Then N-1 lines where each line have 3 integers a b and c, representing that there is a bidirectionoal road between city a and city b, and the cost is c.(a != b and c <= 100000). Then there are M lines of operation. For example, "Change a b c" means changing all the costs of the road which are passed by him when he travels from city a to city b to c. "Query a b" means he wants you to tell him how many different kinds of continuous same cost he has to pay traveling from city a to city b.(if a == b, the cost is 0).
 

Output

He insure you that there is exactly one route between every two different cities.
 

Sample Input

  
  
9 3 1 2 2 2 3 1 1 7 2 1 4 2 3 5 2 3 6 1 5 8 2 5 9 3 Query 1 8 Change 2 6 3 Query 1 6
 

Sample Output

  
  
3 2
 

Source

 

Recommend

wange2014


思路:与HYSBZ 2243 [SDOI2011]染色这一题几乎完全一样,都是树链剖分和线段树的组合应用题。只不过这里将点权操作变成了边权操作,处理起来稍微麻烦一点。在比赛时,一下子就想到了染色这一题,但是因为没有A掉,所以不会改。哎!自己太弱了啊!难点在于链的拼接。在进行链的合并时,判断两个链的端点是否一致,一致的话答案减少一,否则不减少。注意:询问时,两点相同答案为0,但数据可能不存在这种情况,所以不判断也能A掉。详见代码。


附上AC代码:

#include <bits/stdc++.h>
#define lrt rt<<1
#define rrt rt<<1|1
#define lson l, m, lrt
#define rson m+1, r, rrt
using namespace std;
const int maxn = 40005;
// 分别表示以当前节点作为根的子树的节点数目,
// 树上各个节点的初始值,当前节点的重儿子
int sizev[maxn], num[maxn], son[maxn];
// 分别表示树链上深度最小的节点,当前节点的深度,
// 原节点在剖分后的时间戳,即新的编号
int top[maxn], deep[maxn], pos[maxn];
// 分别表示当前时间戳对应的原节点编号,
// 当前节点的父节点
int level[maxn], p[maxn];
// 判断该节点是否被访问过了
bool vis[maxn];
// 分别表示节点数,询问数和时间戳计数
int n, q, cnt;
// 存储树的各个节点所连接的边
vector<int> edge[maxn];

void init(){
	for (int i=1; i<=n; ++i){
		sizev[i] = top[i] = son[i] = 0;
		deep[i] = pos[i] = level[i] = 0;
		p[i]=0, vis[i]=false;
		cnt = 0;
		edge[i].clear();
	}
}

void add_edge(int u, int v){
	edge[u].push_back(v);
	edge[v].push_back(u);
}

void dfs1(int u, int root){
	vis[u] = true;
	sizev[u] = 1;
	p[u] = root;
	deep[u] = deep[root]+1;
	int siz = edge[u].size();
	for (int i=0; i<siz; ++i){
		int v = edge[u][i];
		if (v!=p[u] && !vis[v]){
			dfs1(v, u);
			sizev[u] += sizev[v];
			if (son[u] == 0)
				son[u] = v;
			else if (sizev[son[u]] < sizev[v])
				son[u] = v;
		}
	}
}

void dfs2(int u, int root){
	vis[u] = true;
	pos[u] = ++cnt;
	level[cnt] = u;
	top[u] = root;
	if (son[u])
		dfs2(son[u], root);
	int siz = edge[u].size();
	for (int i=0; i<siz; ++i){
		int v = edge[u][i];
		if (v!=p[u] && v!=son[u] && !vis[v])
			dfs2(v, v);
	}
}

struct node{
	int u, v, c;
} edges[maxn];
int sumv[maxn<<2], setv[maxn<<2];
int lv[maxn<<2], rv[maxn<<2];
char op[10];

void push_up(int rt){
	lv[rt]=lv[lrt], rv[rt]=rv[rrt];
	sumv[rt] = sumv[lrt]+sumv[rrt]-(rv[lrt]==lv[rrt]);
}

void build(int l, int r, int rt){
	setv[rt] = -1;
	if (l == r){
		sumv[rt] = 1;
		lv[rt] = rv[rt] = num[l];
		return ;
	}
	int m = (l+r)>>1;
	build(lson);
	build(rson);
	push_up(rt);
}

void push_down(int l, int r, int rt){
	if (setv[rt] != -1){
		setv[lrt] = setv[rrt] = setv[rt];
		sumv[lrt] = sumv[rrt] = 1;
		lv[lrt] = lv[rrt] = setv[rt];
		rv[lrt] = rv[rrt] = setv[rt];
		setv[rt] = -1;
	}
}

void update(int cl, int cr, int val, int l, int r, int rt){
	if (cl<=l && r<=cr){
		setv[rt] = lv[rt] = rv[rt] = val;
		sumv[rt] = 1;
		return ;
	}
	push_down(l, r, rt);
	int m = (l+r)>>1;
	if (cl <= m)
		update(cl, cr, val, lson);
	if (cr > m)
		update(cl, cr, val, rson);
	push_up(rt);
}

int queries(int ql, int qr, int l, int r, int rt){
	if (ql<=l && r<=qr)
		return sumv[rt];
	push_down(l, r, rt);
	int m = (l+r)>>1;
	int sumr = 0;
	if (ql <= m)
		sumr += queries(ql, qr, lson);
	if (qr > m)
		sumr += queries(ql, qr, rson);
	if (ql<=m && qr>m && rv[lrt]==lv[rrt])
		--sumr;
	return sumr;
}

void change(int x, int y, int val){
	while (top[x] != top[y]){
		if (deep[top[x]] < deep[top[y]])
			swap(x, y);
		update(pos[top[x]], pos[x], val, 1, n, 1);
		x = p[top[x]];
	}
	if (x == y)
		return ;
	if (deep[x] > deep[y])
		swap(x, y);
	update(pos[son[x]], pos[y], val, 1, n, 1);
}

int get_find(int pos, int l, int r, int rt){
	if (l == r)
		return lv[rt];
	push_down(l, r, rt);
	int m = (l+r)>>1;
	if (pos <= m)
		return get_find(pos, lson);
	return get_find(pos, rson);
}

int seek(int x, int y){
	int ans=0, lastu=-1, lastv=-1;
	while (top[x] != top[y]){
		if (deep[top[x]] < deep[top[y]]){
			swap(x, y);
			swap(lastu, lastv);
		}
		ans += queries(pos[top[x]], pos[x], 1, n, 1);
		int col = get_find(pos[x], 1, n, 1);
		if (col == lastu)
			--ans;
		lastu = get_find(pos[top[x]], 1, n, 1);
		x = p[top[x]];
	}
	if (x == y){
		if (lastu == lastv)
			--ans;
		return ans;
	}
	if (deep[x] > deep[y]){
		swap(x, y);
		swap(lastu, lastv);
	}
	ans += queries(pos[son[x]], pos[y], 1, n, 1);
	int col = get_find(pos[y], 1, n, 1);
	if (col == lastv)
		--ans;
	col = get_find(pos[son[x]], 1, n, 1);
	if (col == lastu)
		--ans;
	return ans;
}

int main(){
	while (~scanf("%d%d", &n, &q)){
		init();
		int a, b, c;
		for (int i=1; i<n; ++i){
			scanf("%d%d%d", &a, &b, &c);
			edges[i].u = a;
			edges[i].v = b;
			edges[i].c = c;
			add_edge(a, b);
		}
		dfs1(1, 0);
		memset(vis, false, sizeof(bool)*(n+1));
		dfs2(1, 1);
		for (int i=1; i<n; ++i){
			if (deep[edges[i].u] < deep[edges[i].v])
				swap(edges[i].u, edges[i].v);
			num[pos[edges[i].u]] = edges[i].c;
		}
		build(1, n, 1);
		while (q--){
			scanf("%s%d%d", op, &a, &b);
			if (op[0] == 'Q')
				printf("%d\n", a==b ? 0 : seek(a, b));
			else{
				scanf("%d", &c);
				change(a, b, c);
			}
		}
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值