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
调到心态小崩
蒟蒻瑟瑟发抖

### 关于HDU - 6609 的题目解析 由于当前未提供具体关于 HDU - 6609 题目的详细描述,以下是基于一般算法竞赛题型可能涉及的内容进行推测和解答。 #### 可能的题目背景 假设该题目属于动态规划类问题(类似于多重背包问题),其核心在于优化资源分配或路径选择。此类问题通常会给出一组物品及其属性(如重量、价值等)以及约束条件(如容量限制)。目标是最优地选取某些物品使得满足特定的目标函数[^2]。 #### 动态转移方程设计 如果此题确实是一个变种的背包问题,则可以采用如下状态定义方法: 设 `dp[i][j]` 表示前 i 种物品,在某种条件下达到 j 值时的最大收益或者最小代价。对于每一种新加入考虑范围内的物体 k ,更新规则可能是这样的形式: ```python for i in range(n): for s in range(V, w[k]-1, -1): dp[s] = max(dp[s], dp[s-w[k]] + v[k]) ``` 这里需要注意边界情况处理以及初始化设置合理值来保证计算准确性。 另外还有一种可能性就是它涉及到组合数学方面知识或者是图论最短路等相关知识点。如果是后者的话那么就需要构建相应的邻接表表示图形结构并通过Dijkstra/Bellman-Ford/Floyd-Warshall等经典算法求解两点间距离等问题了[^4]。 最后按照输出格式要求打印结果字符串"Case #X: Y"[^3]。 #### 示例代码片段 下面展示了一个简单的伪代码框架用于解决上述提到类型的DP问题: ```python def solve(): t=int(input()) res=[] cas=1 while(t>0): n,k=list(map(int,input().split())) # Initialize your data structures here ans=find_min_unhappiness() # Implement function find_min_unhappiness() res.append(f'Case #{cas}: {round(ans)}') cas+=1 t-=1 print("\n".join(res)) solve() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值