【SDUT 3494】 简单题III(树链剖分)

【SDUT 3494】 简单题III(树链剖分)


简单题III

Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^

题目描述

RE最近在写一款名叫《我不要娶公主》的RPG游戏(听名字就知道这货单身久
 
了准备FFF),既然是RPG类游戏那么少不了的就是小地图的自动寻路。耿直的RE决定暴力掉这个
 
算法。显然是不行的。所以,这便求到我们这群人。在这个RPG的地图上共有N个复活点,N-1条路
 
,链接整张的地图,保证每两点可达,已知每一条路的长度,战斗就发生在各个复活点和通往复
 
活点的路上。玩家之间的战斗可以改变道路的地形,这便会改变每一条路的长度,那么问题来了
 
。我们如何让玩家得知自己所在的复活点距离自己的目的地多远。

输入

多组输入。每组首先输入两个整数n,m。n为复活点数(n<100001),m为操作数(m<100001)。
 
随后按照书序输入n-1组三个整数u,v,w,表示u复活点与v复活点之间的路程为w。(1<=w<=10000),

最后输入m步操作,每一步操作首先输入一个数字k((k=0||k=1)
 
当k = 0时,输入两个数x,y,表示查询从复活点x到复活点y的总路程。
 
当k = 1时,输入两个数x,y,表示玩家的战斗导致第x条路的长度变成了y。
 

输出

输出玩家查询的从x复活点到y复活点的距离。

示例输入

3 3
1 2 1
2 3 2
0 1 2
1 2 3
0 2 3

示例输出

1
3

可算是搞出来了一道树剖的题。首先在这里鸣谢JinZeyu金巨巨QAQQAsQ巨耐心给窝讲解,实话说,我看一天论文都赶不上这么几分钟的讲解!论文太泛了,许多明白的地方讲的很细,我个人不太懂的地方又一笔带过,所以一直不太又耐心看整篇的论文(也是个缺点

扯远了,自家出的题,当时用LCA水过去了,也是数据较弱。赛后黑腹地跑了组数据把所有LCA给卡掉了(就在刚刚~。~
树链剖分真心是个好东西(虽然到现在我还只会这么裸着用。。
第一遍dfs,找出所有点的深度、父亲、重儿子、子树节点数
第二遍dfs,贪心地往重儿子方向跑(保证每条重链上的点dfs序连续,也是为了区间修改和查询做准备)标记每个点的dfs序,同时标记每个点所在重链的链顶。

至于上面的重儿子 重链。。百度吧=.=
突然感觉会树剖的看我刚才说的就是废话,不会树剖的看就是天书=.=(好欠揍。。。
总之这么跑完后用线段树或树状数组之类的数据结构维护一下区间,然后区间更新或查询就好,。

说了一通废话,上代码吧。。
#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread() freopen("data2.in","r",stdin)
#define fwrite() freopen("data2.out","w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 1e9+7;
const double eps = 1e-8;

struct Edge
{
	int v,w,next;
};

int head[233333];
Edge eg[233333];

//点深度       链顶        子树节点数   重儿子 		父节点		代表的边权	访问编号	
int dep[233333],top[233333],siz[233333],son[233333],fa[233333],val[233333],tid[233333];
int bit[444444];
int n,m,tim;

void dfs1(int u,int pre,int d)
{
	//父亲
	fa[u] = pre;
	//深度
	dep[u] = d;
	//子树点个数
	siz[u] = 1;
	//重儿子
	son[u] = u;

	int tmp = -1;
	int v,w;
	for(int i = head[u]; ~i; i = eg[i].next)
	{
		v = eg[i].v;
		if(v == pre) continue;
		dfs1(v,u,d+1);
		siz[u] += siz[v];
		if(siz[v] > tmp)
		{
			tmp = siz[v];
			son[u] = v;
		}
	}
}

void dfs2(int u,int d)
{
	top[u] = d;
	tid[u] = ++tim;
	if(son[u] == u) return;
	//贪心先找重儿子
	dfs2(son[u],d);
	int v;
	for(int i = head[u]; ~i; i = eg[i].next)
	{
		v = eg[i].v;
		if(v != fa[u] && v != son[u]) dfs2(v,v);

		val[tid[v]] = eg[i].w;
	}
}

void build(int root,int l,int r)
{
	if(l == r)
	{
		bit[root] = val[l];
		return;
	}
	int mid = (l+r)>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	bit[root] = bit[root<<1]+bit[root<<1|1];
}

void update(int root,int l,int r,int ll,int d)
{
	if(l == r)
	{
		bit[root] = d;
		return;
	}
	int mid = (l+r)>>1;
	if(mid >= ll) update(root<<1,l,mid,ll,d);
	else update(root<<1|1,mid+1,r,ll,d);
	bit[root] = bit[root<<1]+bit[root<<1|1];
}

int query(int root,int l,int r,int ll,int rr)
{
	if(l == ll && r == rr)
	{
		return bit[root];
	}
	int mid = (l+r)>>1;
	if(mid >= rr) return query(root<<1,l,mid,ll,rr);
	else if(mid+1 <= ll) return query(root<<1|1,mid+1,r,ll,rr);
	else return query(root<<1,l,mid,ll,mid)+query(root<<1|1,mid+1,r,mid+1,rr);
}

void init()
{
	tim = 0;
	dfs1(1,1,1);
	val[1] = 0;
	dfs2(1,1);
	build(1,1,tim);
}

int main()
{
	fread();
	fwrite();

	int u,v,w,k,x,y;
	while(~scanf("%d%d",&n,&m))
	{
		memset(head,-1,sizeof(head));
		int tp = 2;
		for(int i = 1; i < n; ++i)
		{
			scanf("%d%d%d",&u,&v,&w);
			eg[tp].v = v;
			eg[tp].w = w;
			eg[tp].next = head[u];
			head[u] = tp++;

			eg[tp].v = u;
			eg[tp].w = w;
			eg[tp].next = head[v];
			head[v] = tp++;
		}

		init();
		while(m--)
		{
			scanf("%d%d%d",&k,&x,&y);
			if(k == 1)
			{
				u = eg[x<<1].v;
				v = eg[x<<1|1].v;
				val[max(tid[u],tid[v])] = y;
				update(1,1,n,max(tid[u],tid[v]),y);
			}
			else
			{
				int ans = 0;
				while(top[x] != top[y])
				{
					if(dep[top[x]] < dep[top[y]]) swap(x,y);
					ans += query(1,1,n,tid[top[x]],tid[x]);
					x = fa[top[x]];
				}
				if(tid[x] < tid[y]) swap(x,y);
				ans += query(1,1,n,tid[y],tid[x])-val[tid[y]];
				printf("%d\n",ans);
			}
		}
	}

	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值