[Usaco2009 Jan]安全路经Travel BZOJ1576 Dijkstra+树链剖分+线段树

分析:

Dijkstra求最短路树,在最短路树上进行操作,详情可见上一篇博客:http://www.cnblogs.com/Winniechen/p/9042937.html

我觉得这个东西不压行写出了有点丑...之后写了一个压行后更丑的...

附上压行后的代码:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
#define N 200005
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define inf 0x3f3f3f3f
struct node
{
	int to,next,val;
}E[N<<2],e[N<<1];
int head[N],head1[N],cnt,cnt1,fa[N],a[N];
int dep[N],anc[N],siz[N],son[N],idx[N],b[N];
int dis[N],minn[N<<2],cov[N<<2],n,vis[N],c[N];
void add1(int x,int y,int z){E[cnt1].to=y;E[cnt1].next=head1[x];E[cnt1].val=z;head1[x]=cnt1++;}
void add(int x,int y,int z){e[cnt].to=y;e[cnt].next=head[x];e[cnt].val=z;head[x]=cnt++;}
void Dijkstra()
{
	memset(dis,0x3f,sizeof(dis));int num=0;
	priority_queue<pair<int ,int > >q;dis[1]=0;q.push(make_pair(0,1));
	while(!q.empty())
	{
		if(num==n)break;
		int x=q.top().second;q.pop();
		if(vis[x])continue;vis[x]=1;num++;
		for(int i=head1[x];i!=-1;i=E[i].next)
		{
			int to1=E[i].to;
			if(dis[to1]+E[i].val==dis[x])add(to1,x,E[i].val),add(x,to1,E[i].val);
		}
		for(int i=head1[x];i!=-1;i=E[i].next)
		{
			int to1=E[i].to;
			if(dis[x]+E[i].val<dis[to1])
			{
				dis[to1]=dis[x]+E[i].val;
				q.push(make_pair(-dis[to1],to1));
			}
		}
	}
}
void dfs1(int x,int from)
{
	fa[x]=from,dep[x]=dep[from]+1,siz[x]=1;
	for(int i=head[x];i!=-1;i=e[i].next)
	{
		int to1=e[i].to;
		if(to1!=from)
		{
			dfs1(to1,x);siz[x]+=siz[to1];
			if(siz[son[x]]<siz[to1])son[x]=to1;
		}
	}
}
int tims;
void dfs2(int x,int top)
{
	anc[x]=top;idx[x]=++tims;
	if(son[x])dfs2(son[x],top);
	for(int i=head[x];i!=-1;i=e[i].next)
	{
		int to1=e[i].to;
		if(to1!=fa[x]&&to1!=son[x])dfs2(to1,to1);
	}
}
void PushDown(int rt)
{
	if(cov[rt]!=inf)
	{
		int t=cov[rt];
		cov[rt<<1]=min(cov[rt<<1],t);
		minn[rt<<1]=min(minn[rt<<1],t);
		cov[rt<<1|1]=min(cov[rt<<1|1],t);
		minn[rt<<1|1]=min(minn[rt<<1|1],t);
		cov[rt]=inf;
	}
}
void build(int l,int r,int rt)
{
	minn[rt]=cov[rt]=inf;
	if(l==r)return ;int m=(l+r)>>1;
	build(lson);build(rson);
}
void Update(int L,int R,int c,int l,int r,int rt)
{
	if(L<=l&&r<=R)
	{
		minn[rt]=min(minn[rt],c);cov[rt]=min(cov[rt],c);
		return ;
	}
	PushDown(rt);int m=(l+r)>>1;
	if(L<=m)Update(L,R,c,lson);
	if(m<R)Update(L,R,c,rson);
}
int query(int x,int l,int r,int rt)
{
	if(l==r)return minn[rt];
	PushDown(rt);int m=(l+r)>>1;
	if(m>=x)return query(x,lson);
	else return query(x,rson);
}
void get_lca(int x,int y,int c)
{
	while(anc[x]!=anc[y])
	{
		if(dep[anc[x]]<dep[anc[y]])swap(x,y);
		Update(idx[anc[x]],idx[x],c,1,n,1);x=fa[anc[x]];
	}
	if(dep[x]>dep[y])swap(x,y);
	if(x!=y)Update(idx[x]+1,idx[y],c,1,n,1);
}
int main()
{
	int m;memset(head,-1,sizeof(head));memset(head1,-1,sizeof(head1));scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		add1(x,y,z);add1(y,x,z);a[i]=x,b[i]=y,c[i]=z;
	}
	Dijkstra();dfs1(1,0);dfs2(1,1);build(1,n,1);
	for(int i=1;i<=m;i++)
	{
		if(abs(dis[a[i]]-dis[b[i]])==c[i])continue;
		get_lca(a[i],b[i],dis[a[i]]+dis[b[i]]+c[i]);
	}
	for(int i=2;i<=n;i++)
	{
		int t=query(idx[i],1,n,1);
		t==inf?printf("-1\n"):printf("%d\n",t-dis[i]);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/Winniechen/p/9042949.html

时间: 2024-11-13 16:21:55

[Usaco2009 Jan]安全路经Travel BZOJ1576 Dijkstra+树链剖分+线段树的相关文章

Aizu 2450 Do use segment tree 树链剖分+线段树

Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show.php?pid=39566 Description Given a tree with n (1 ≤ n ≤ 200,000) nodes and a list of q (1 ≤ q ≤ 100,000) queries, process the queries in order and out

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include

【bzoj3589】动态树 树链剖分+线段树

题目描述 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0:这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1:小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次. 输入 第一行一个整数n(1<=n<=200,000), 即节点数. 接下来n-1行, 每行两个数字u,

BZOJ2243 (树链剖分+线段树)

Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. 解题分析 树链剖分+线段树. 开一个记录类型,记录某一段区间的信息.l 表示区间最左侧的颜色 , r 表示区间最右侧的颜色 , sum 表示区间中颜色段数量. 合并时判断一下左区间的右端点和有区间的左端点的颜色是否一样. 树上合并时需要用两个变量ans1,ans2来存储.ans1表示x往上走时形成的链的信息,

bzoj4304 (树链剖分+线段树)

Problem T2 (bzoj4304 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 解题分析 练手题.树链剖分+线段树. 参考程序 1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #incl

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

题目描述 由乃正在做她的OJ.现在她在处理OJ上的用户排名问题.OJ上注册了n个用户,编号为1-",一开始他们按照编号 排名.由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为Deus天 天问她题...因为Deus天天问由乃OI题,所以由乃去学习了一下OI,由于由乃智商挺高,所以OI学的特别熟练她 在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送 Deus:这个题怎么做呀? yuno:这个不是NOI2014的水题吗... Deu

HDU 2460 Network(双连通+树链剖分+线段树)

HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链剖分+线段树处理 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; #pragma comment(linke

【bzoj1959】[Ahoi2005]LANE 航线规划 离线处理+树链剖分+线段树

题目描述 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系. 星际空间站的Samuel II巨型计算机经过长期探测,已经锁定了Samuel星系中许多星球的空间坐标,并对这些星球从1开始编号1.2.3……. 一些先遣飞船已经出发,在星球之间开辟探险航线. 探险航线是双向的,例如从1号星球到3号星球开辟探险航线,那么从3号星球到1号星球也可以使用这条航线. 例如下图所示: 在5个星球之间,有5条探险航