HDU -5893 List wants to travel

List wants to travel

Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1521 Accepted Submission(s): 439

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
2016 ACM/ICPC Asia Regional Shenyang Online

树链剖分+线段树
难点和坑点在于怎么合并路径orz

合并区间的时候,我们是根据树链剖分把从起点到终点的路径一段一段合并
ans1从起点开始合并,所以新添加的一段路径应该添加在ans1的右边
ans2从终点开始合并,所以新添加的一段路径应该添加在ans2的左边
然后合并新的路径的时候,我们是把线段树上的一段段区间合并起来
如果是从起点开始合并的话,新的路径是从下往上的,也就是说dfs序是从大到小的
然而用线段树上添加的路径时候,我先查询的左区间,再查询的右区间,所以线段树查询到的段落的l和r是从小到大的
也就是说是线段树查询到的段落合并起来的路径在原来的树上,是从上往下的,dfs序从小到大
所以我们每在线段树上查询到一个段落都要把它左右端点交换一下,每次合并,新的都添加在原来的左边,这样合并出来的路径就是从下往上,是从起点开始走的路径
同理,从终点开始合并的话,新的路径应该是从上往下,dfs序从小到大,添加在ans2的左边。(模拟从终点走一遍就知道了)
所以如果是从终点开始合并的话,我们就不用翻转了,直接拿线段树上的合并就可以了,每次新添加的段落都添加在原来的右边

然后两条路径最终都合并到同一条重链上了。
如果两条路径相交在同一个点,则直接合并两条路径就可以了
如果ans2在下面,则还要合并一条从上往下的路径,所以直接合并线段树的就可以了,然后添加在ans1的右边,ans1再与ans2合并
如果ans1在下面,则还要合并一条从下往上的路径,所以直接合并线段树的就可以了,然后添加在ans1的右边,ans1再与ans2合并

#include<bits/stdc++.h>
using namespace std;
#define mem(a) memset(a,0,sizeof(a))

const int MAXN = 40100;
vector<int> g[MAXN];
int n,size[MAXN],dfn[MAXN],top[MAXN],son[MAXN],fa[MAXN],d[MAXN],num = 0,aa[MAXN];
map<pair<int,int>,int> w;

//树链剖分
void dfs1(int x)
{
	d[x] = d[fa[x]] + 1;
	size[x] = 1;
	for (int i = 0;i<g[x].size();i++)
	{
		int v = g[x][i];
		if (v == fa[x]) continue;
		fa[v] = x;
		dfs1(v);
		size[x] += size[v];
		if (size[v] > size[son[x]]) son[x] = v;
	}
}

void dfs2(int x,int tp)
{
	dfn[x] = ++num;
	top[x] = tp;
	aa[num] = w[make_pair(x,fa[x])];
	if (son[x]) dfs2(son[x],tp);
	for (int i = 0;i<g[x].size();i++)
		if (g[x][i] != fa[x] && g[x][i] != son[x]) dfs2(g[x][i],g[x][i]);
}
 
struct node{
	int l,r,lazy,num,nl,nr;
	int mid(){ return (r - l) / 2 + l;}
	void merge(node a,node b)//合并两个区间 
	{
		num = a.num + b.num;
		if (a.nr == b.nl && a.nr != -1 && b.nl != -1) num--;
		nl = a.nl; nr = b.nr;
	}
	void overturn() {swap(nr,nl);}//将区间顺序倒置 
	void write() //检验时用 
	{
		printf("%d %d %d %d %d %d\n",l,r,lazy,num,nl,nr);
	}
};

struct seg_tree{
	node tree[MAXN * 4];
	void build(int st,int ed,int x)//建树,lazy表示当前区间有没有被翻转,nl表示左端点的数字,nr表示右端点的数字,num表示有多少段颜色相同的 
	{
		tree[x].l = st;
		tree[x].r = ed;
		tree[x].lazy = -1;
		if (st == ed)
		{
			tree[x].nl = tree[x].nr = aa[st];
			tree[x].num = 1;
			if (st == 1)//建树的时候,一条边上两点,深度较大的那个点的dfn作为这条线段的dfn,所以根节点1没有对应的边 
			{
				tree[x].nl = tree[x].nr = -1;
				tree[x].num = 0;
			}
			return;
		}
		int mid = tree[x].mid();
		build(st,mid,x * 2);
		build(mid + 1,ed,x * 2 + 1);
		tree[x].merge(tree[x * 2],tree[x * 2 + 1]);
	}
	void modify(int st,int ed,int x,int c)//区间修改值 
	{
		if (tree[x].l >= st && tree[x].r <= ed)
		{
			tree[x].nl = tree[x].nr = c;
			tree[x].num = 1;
			tree[x].lazy = c;
			return;
		}
		if (tree[x].lazy != -1)
		{
			tree[x*2].lazy=tree[x*2+1].lazy=tree[x*2].nl=tree[x*2].nr=tree[x*2+1].nl=tree[x*2+1].nr=tree[x].lazy;
			tree[x * 2].num = tree[x * 2 + 1].num = 1;
			tree[x].lazy = -1;
		}
		int mid = tree[x].mid();
		if (mid >= st) modify(st,ed,x * 2,c);
		if (ed > mid) modify(st,ed,x * 2 + 1,c);
		tree[x].merge(tree[x * 2],tree[x * 2 + 1]);
	}
	node query(int st,int ed,int x,char c)//查询区间 
	{
		if (tree[x].l >= st && tree[x].r <= ed)
		{
			node temp = tree[x];
			if (c == 'L') temp.overturn();//如果是从起始点开始合并 
			return temp;
		}
		if (tree[x].lazy != -1)
		{
			tree[x*2].lazy=tree[x*2+1].lazy=tree[x*2].nl=tree[x*2].nr=tree[x*2+1].nl=tree[x*2+1].nr=tree[x].lazy;
			tree[x * 2].num = tree[x * 2 + 1].num = 1;
			tree[x].lazy = -1;
		}
		int mid = tree[x].mid();
		node ans;
		ans.nl = -1; ans.nr = -1; ans.num = 0;
		if (mid >= st) ans = query(st,ed,x * 2,c);
		if (ed > mid)
		{
			if (ans.num == 0) ans = query(st,ed,x * 2 + 1,c);
			else if (c == 'L') ans.merge(query(st,ed,x * 2 + 1,c),ans);
			else if (c == 'R') ans.merge(ans,query(st,ed,x * 2 + 1,c));
		}
		tree[x].merge(tree[x * 2],tree[x * 2 + 1]);
		return ans;
	}
}seg;


int mapping(int x,int y,int c)//c表示当前是修改还是查询 
{
	int fx = top[x],fy = top[y];
	node ans1,ans2;
	ans1.nl = ans1.nr = ans2.nl = ans2.nr = -1;//ans1表示从起始点开始合并,ans2表示从终点开始合并 
	ans1.num = ans2.num = 0;
	while (fx != fy)
	{
		if (d[fx] > d[fy])
		{
			if (c != -1) seg.modify(dfn[fx],dfn[x],1,c);
			else
			{
				if (ans1.num == 0) ans1 = seg.query(dfn[fx],dfn[x],1,'L'); //如果是从起点开始合并 
				else ans1.merge(ans1,seg.query(dfn[fx],dfn[x],1,'L'));
			}
			x = fa[fx];
			fx = top[x];
		}
		else
		{
			if (c != -1) seg.modify(dfn[fy],dfn[y],1,c);
			else
			{
				if (ans2.num == 0) ans2 = seg.query(dfn[fy],dfn[y],1,'R');//如果是从终点开始合并 
				else ans2.merge(seg.query(dfn[fy],dfn[y],1,'R'),ans2);
			}
			y = fa[fy];
			fy = top[y];
		}
	}
	if (x == y)//如果两条路径相交在同一个点 
	{
		if (c != -1) return 0;
		if (ans1.num == 0) return ans2.num;
		else if (ans2.num == 0) return ans1.num;
		ans1.merge(ans1,ans2);
		return ans1.num;
	}
	if (d[x] < d[y])//y在下面
	{
		if (c != -1)
		{
			seg.modify(dfn[x] + 1,dfn[y],1,c);
			return 0;
		}
		if (ans1.num == 0) ans1 = seg.query(dfn[x] + 1,dfn[y],1,'R');
		else ans1.merge(ans1,seg.query(dfn[x] + 1,dfn[y],1,'R'));
		if (ans2.num == 0) return ans1.num;
		else ans1.merge(ans1,ans2);
	}
	else//x在下面 
	{
		if (c != -1)
		{
			seg.modify(dfn[y] + 1,dfn[x],1,c);
			return 0;
		}
		if (ans1.num == 0) ans1 = seg.query(dfn[y] + 1,dfn[x],1,'L');
		else ans1.merge(ans1,seg.query(dfn[y] + 1,dfn[x],1,'L'));
		if (ans2.num != 0) ans1.merge(ans1,ans2);
	}
	return ans1.num;
}

int main()
{
	int n,p;
	while (scanf("%d%d",&n,&p) != EOF) 
	{
		for (int i = 1,u,v,t;i<=n-1;i++)
		{
			scanf("%d%d%d",&u,&v,&t);
			w[make_pair(u,v)] = t;
			w[make_pair(v,u)] = t;
			g[u].push_back(v);
			g[v].push_back(u);
		}
		dfs1(1);
		dfs2(1,1);
		char c[10];
		seg.build(1,n,1);
		while (p--)
		{
			scanf("%s",&c);
			if (c[0] == 'Q')
			{
				int u,v;
				scanf("%d%d",&u,&v);
				if (u == v)  printf("0\n");
				else printf("%d\n",mapping(u,v,-1));
			}
			else if (c[0] == 'C')
			{
				int u,v,t;
				scanf("%d%d%d",&u,&v,&t);
				int temptemp = mapping(u,v,t);
			}
		}
		for (int i = 1;i<=n;i++) g[i].clear();
		mem(son); mem(size); mem(d); mem(fa); mem(dfn); mem(top); mem(aa);
		num = 0;
		w.clear();
	}
}

这题是真的坑…线段树上的顺序也要注意一直都忽略了orz
调到心态小崩
蒟蒻瑟瑟发抖

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值