【BZOJ】1576 [Usaco2009 Jan]安全路经Travel
【算法】最短路树+(树链剖分+线段树)||最短路树+并查集
【题解】
两种方法的思想是一样的,首先题目限制了最短路树唯一。
那么建出最短路树后,就是询问对于每个点断掉父边后重新找路径的最小值,其它路径只能是这个点和其子树节点通过非树边到达非子树节点。
这样考虑很难统计,换个角度考虑每条非树边的影响。
一条非树边连接两个端点u,v,它们会有一个LCA,那么这条非树边就可以影响u~LCA和v~LCA两条链上的点。
这样依然不方便统计,因为两条链上每个点的影响各不相同,所以使用差分的思想。
定义一条非树边对两条链上的点的贡献为g[i]=dis[u]+dis[v]+e[i].w,那么对于两条链上的每个点就是ans[x]=min(ans[x],g[i]-dis[x]),因为dis[x]是每个点自身属性,那么就可以统一地对两条链上上的点赋值g[i]。
现在,我们可以明确每条非树边对特定的两条边的贡献,那么显然可以用树链剖分+线段树对两条链上的点进行【区间最小值覆盖+单点查询最小值】,这一操作可以用标记永久化实现。
考虑另一种写法,如果我们把非树边按贡献排序,那么贡献小的覆盖之后,贡献大的就不可能影响到这些被覆盖过的点了,那么可以将覆盖过的点用并查集合并为一个点,遇到直接跳。
复杂度O(m log n)。
<并查集>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cctype>
#include<cstring>
using namespace std;
const int maxn=,inf=0x3f3f3f3f;
struct edge{int u,v,w,from;}e[maxn*];
int n,m,cnt,ans[maxn],first[maxn],tot,fa[maxn],f[maxn],deep[maxn],dis[maxn],d[maxn],c[maxn];
int read()
{
char c;int s=,t=;
while(!isdigit(c=getchar()))if(c=='-')t=-;
do{s=s*+c-'';}while(isdigit(c=getchar()));
return s*t;
}
void insert(int u,int v,int w)
{tot++;e[tot].u=u;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
struct cyc{
int x,d;
bool operator < (const cyc &a)const{
return d>a.d;
}
};
priority_queue<cyc>q;
void dijkstra(){
memset(d,0x3f,sizeof(d));
deep[]=d[]=;q.push((cyc){,});
while(!q.empty()){
cyc x=q.top();q.pop();
if(x.d!=d[x.x])continue;
for(int i=first[x.x];i;i=e[i].from)if(d[e[i].v]>d[x.x]+e[i].w){
d[e[i].v]=d[x.x]+e[i].w;
deep[e[i].v]=deep[x.x]+;
f[e[i].v]=x.x;
q.push((cyc){e[i].v,d[e[i].v]});
}
}
}
struct cyc2{int u,v,num;}b[maxn];
bool cmp(cyc2 a,cyc2 b){return a.num<b.num;}
int main(){
n=read();m=read();
int u,v,w;
for(int i=;i<=m;i++){
u=read();v=read();w=read();
insert(u,v,w);insert(v,u,w);
}
dijkstra();
for(int i=;i<=tot;i+=){
if(deep[e[i].u]<deep[e[i].v])swap(e[i].u,e[i].v);
if(d[e[i].u]!=d[e[i].v]+e[i].w)b[++cnt]=(cyc2){e[i].u,e[i].v,d[e[i].u]+d[e[i].v]+e[i].w};
}
sort(b+,b+cnt+,cmp);
for(int i=;i<=n;i++)fa[i]=i;
f[]=;//初始父亲
for(int i=;i<=cnt;i++){
int x=find(b[i].u),y=find(b[i].v);
while(x!=y){
if(deep[x]<deep[y])swap(x,y);
if(!ans[x])ans[x]=b[i].num;
x=fa[x]=find(f[x]);
}
}
for(int i=;i<=n;i++)if(!ans[i])printf("-1\n");else printf("%d\n",ans[i]-d[i]);
return ;
}
补充说明:【排序+并查集】是一种套路,处理MST的著名算法kruskal就是使用这种思想。这种做法要求无后效性,将价值最大的边纳入然后并成一个点继续处理,从而保证最优性。
<树链剖分+线段树>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int inf=0x3f3f3f3f,maxn=,maxm=;
struct edge{int u,from,v,w;}e[maxm*];
struct tree{int l,r,tag;}t[maxn*];
int n,m,tot=,first[maxn],q[],fa[maxn],deep[maxn],d[maxn],top[maxn],pos[maxn],size[maxn],te[maxn],dfsnum=;
bool mark[maxm*],vis[maxn];
void insert(int u,int v,int w)
{tot++;e[tot].u=u;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
void spfa()
{
memset(d,0x3f,sizeof(d));
memset(mark,,sizeof(mark));
memset(vis,,sizeof(vis));
int head=,tail=;q[]=;vis[]=;d[]=;
while(head!=tail)
{
int x=q[head++];if(head>)head=;
for(int i=first[x];i;i=e[i].from)
if(d[e[i].v]>d[x]+e[i].w)
{
int y=e[i].v;
d[y]=d[x]+e[i].w;
fa[y]=x;
mark[te[y]]=;
te[y]=i;
mark[i]=;
if(!vis[y]){q[tail++]=y;if(tail>)tail=;}
vis[y]=;
}
vis[x]=;
}
// for(int i=1;i<=n;i++)printf("fa[%d]=%d d[%d]=%d\n",i,fa[i],i,d[i]);
// for(int i=1;i<=tot;i++)printf("[%d]%d %d %d\n",i,e[i].u,e[i].v,mark[i]);
}
void build(int k,int l,int r)
{
t[k].l=l;t[k].r=r;t[k].tag=inf;
if(l==r)return;
int mid=(l+r)>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
}
void dfs1(int x)
{
size[x]=;
for(int i=first[x];i;i=e[i].from)
if(mark[i])
{
int y=e[i].v;
deep[y]=deep[x]+;
dfs1(y);
size[x]+=size[y];
}
}
void dfs2(int x,int tp)
{
int k=;
top[x]=tp;
pos[x]=++dfsnum;
for(int i=first[x];i;i=e[i].from)
if(mark[i]&&size[e[i].v]>size[k])k=e[i].v;
if(k==)return;
dfs2(k,tp);
for(int i=first[x];i;i=e[i].from)
if(mark[i]&&e[i].v!=k)dfs2(e[i].v,e[i].v);
}
void seg_insert(int k,int l,int r,int x)
{
if(l<=t[k].l&&t[k].r<=r)
{
t[k].tag=min(t[k].tag,x);
return;
}
else
{
int mid=(t[k].l+t[k].r)>>;
if(l<=mid)seg_insert(k<<,l,r,x);
if(r>mid)seg_insert(k<<|,l,r,x);
}
}
int seg_ask(int k,int x)
{
if(t[k].l==t[k].r)return t[k].tag;
int mid=(t[k].l+t[k].r)>>;
if(x<=mid)return min(t[k].tag,seg_ask(k<<,x));
else return min(t[k].tag,seg_ask(k<<|,x));
}
void solve_ins(int x,int y,int w)
{
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])swap(x,y);
seg_insert(,pos[top[x]],pos[x],w);
x=fa[top[x]];
}
if(pos[x]>pos[y])swap(x,y);
if(pos[x]<pos[y])seg_insert(,pos[x]+,pos[y],w);
}
int main()
{
scanf("%d%d",&n,&m);
int u,v,w;
for(int i=;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
insert(u,v,w);insert(v,u,w);
}
spfa();
build(,,n);dfs1();dfs2(,);//printf("sldf\n");
for(int i=;i<=m;i++)
if(!mark[i*-]&&!mark[i*])solve_ins(e[i*].u,e[i*].v,d[e[i*].u]+d[e[i*].v]+e[i*].w);
// printf("asfjld\n");
for(int i=;i<=n;i++)
{
int ans=seg_ask(,pos[i]);
if(ans>inf-)ans=d[i]-;
printf("%d\n",ans-d[i]);
}
return ;
}
SPFA+链剖+线段树
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f,maxn=,maxm=;
struct edge{int u,from,v,w;}e[maxm*];
struct tree{int l,r,tag;}t[maxn*];
int n,m,tot=,first[maxn],fa[maxn],deep[maxn],d[maxn],top[maxn],pos[maxn],size[maxn],te[maxn],dfsnum=;
bool mark[maxm*],vis[maxn];
void insert(int u,int v,int w)
{tot++;e[tot].u=u;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
struct Node{int x,d;}cyc;
priority_queue<Node>q;
bool operator <(Node a,Node b)
{return a.d>b.d;}
void dijkstra()
{
memset(d,0x3f,sizeof(d));
memset(mark,,sizeof(mark));
d[]=;cyc.d=;cyc.x=;q.push(cyc);
while(!q.empty())
{
cyc=q.top();q.pop();
int x=cyc.x;
if(cyc.d!=d[x])continue;
for(int i=first[x];i;i=e[i].from)
if(d[e[i].v]>d[x]+e[i].w)
{
int y=e[i].v;
d[y]=d[x]+e[i].w;
cyc.x=y;cyc.d=d[y];q.push(cyc);
mark[te[y]]=;
te[y]=i;mark[i]=;
fa[y]=x;
}
}
// for(int i=1;i<=n;i++)printf("fa[%d]=%d d[%d]=%d\n",i,fa[i],i,d[i]);
// for(int i=1;i<=tot;i++)printf("[%d]%d %d %d\n",i,e[i].u,e[i].v,mark[i]);
}
void build(int k,int l,int r)
{
t[k].l=l;t[k].r=r;t[k].tag=inf;
if(l==r)return;
int mid=(l+r)>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
}
void dfs1(int x)
{
size[x]=;
for(int i=first[x];i;i=e[i].from)
if(mark[i])
{
int y=e[i].v;
deep[y]=deep[x]+;
dfs1(y);
size[x]+=size[y];
}
}
void dfs2(int x,int tp)
{
int k=;
top[x]=tp;
pos[x]=++dfsnum;
for(int i=first[x];i;i=e[i].from)
if(mark[i]&&size[e[i].v]>size[k])k=e[i].v;
if(k==)return;
dfs2(k,tp);
for(int i=first[x];i;i=e[i].from)
if(mark[i]&&e[i].v!=k)dfs2(e[i].v,e[i].v);
}
void seg_insert(int k,int l,int r,int x)
{
if(l<=t[k].l&&t[k].r<=r)
{
t[k].tag=min(t[k].tag,x);
return;
}
else
{
int mid=(t[k].l+t[k].r)>>;
if(l<=mid)seg_insert(k<<,l,r,x);
if(r>mid)seg_insert(k<<|,l,r,x);
}
}
int seg_ask(int k,int x)
{
if(t[k].l==t[k].r)return t[k].tag;
int mid=(t[k].l+t[k].r)>>;
if(x<=mid)return min(t[k].tag,seg_ask(k<<,x));
else return min(t[k].tag,seg_ask(k<<|,x));
}
void solve_ins(int x,int y,int w)
{
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])swap(x,y);
seg_insert(,pos[top[x]],pos[x],w);
x=fa[top[x]];
}
if(pos[x]>pos[y])swap(x,y);
if(pos[x]<pos[y])seg_insert(,pos[x]+,pos[y],w);
}
int main()
{
scanf("%d%d",&n,&m);
int u,v,w;
for(int i=;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
insert(u,v,w);insert(v,u,w);
}
dijkstra();
build(,,n);dfs1();dfs2(,);//printf("sldf\n");
for(int i=;i<=m;i++)
if(!mark[i*-]&&!mark[i*])solve_ins(e[i*].u,e[i*].v,d[e[i*].u]+d[e[i*].v]+e[i*].w);
// printf("asfjld\n");
for(int i=;i<=n;i++)
{
int ans=seg_ask(,pos[i]);
if(ans>inf-)ans=d[i]-;
printf("%d\n",ans-d[i]);
}
return ;
}
Dijkstra+链剖+线段树
事实证明,Dijkstra比SPFA稳得多,虽然也可能是故意卡的,但终归卡不了Dijkstra,因为本来理论上界就小。
Dijkstra+Heap 2.5s
SPFA+SLF 10s
SPFA TLE
【BZOJ】1576 [Usaco2009 Jan]安全路经Travel的更多相关文章
- bzoj 1576: [Usaco2009 Jan]安全路经Travel 树链剖分
1576: [Usaco2009 Jan]安全路经Travel Time Limit: 10 Sec Memory Limit: 64 MB Submit: 665 Solved: 227[Sub ...
- [BZOJ 1576] [Usaco2009 Jan] 安全路经Travel 【树链剖分】
题目链接: BZOJ - 1576 题目分析 首先Orz Hzwer的题解. 先使用 dijikstra 求出最短路径树. 那么对于一条不在最短路径树上的边 (u -> v, w) 我们可以先沿 ...
- bzoj 1576 [Usaco2009 Jan]安全路经Travel(树链剖分,线段树)
[题意] 给定一个无向图,找到1-i所有的次短路经,要求与最短路径的最后一条边不重叠. [思路] 首先用dijkstra算法构造以1为根的最短路树. 将一条无向边看作两条有向边,考察一条不在最短路树上 ...
- BZOJ.1576.[Usaco2009 Jan]安全路经Travel(树形DP 并查集)
题目链接 BZOJ 洛谷 先求最短路树.考虑每一条非树边(u,v,len),设w=LCA(u,v),这条边会对w->v上的点x(x!=w)有dis[u]+dis[v]-dis[x]+len的距离 ...
- bzoj 1576: [Usaco2009 Jan]安全路经Travel——并查集+dijkstra
Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第i行包含一个数 ...
- bzoj 1576: [Usaco2009 Jan]安全路经Travel【spfa+树链剖分+线段树】
这几天写USACO水题脑子锈住了--上来就贪心,一交就WA 事实上这个是一个叫最短路树的东西,因为能保证只有一条最短路,所以所有最短路合起来是一棵以1为根的树,并且在这棵树上,每个点被精灵占据的路是它 ...
- BZOJ 1576: [Usaco2009 Jan]安全路经Travel
日常自闭半小时后看题解,太弱了qwq. 感觉这道题还是比较难的,解法十分巧妙,不容易想到. 首先题目说了起点到每个点的最短路都是唯一的,那么对这个图求最短路图必定是一棵树,而且这棵树是唯一的. 那么我 ...
- 【BZOJ1576】[Usaco2009 Jan]安全路经Travel 最短路+并查集
[BZOJ1576][Usaco2009 Jan]安全路经Travel Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, ...
- 【思维题 并查集 图论】bzoj1576: [Usaco2009 Jan]安全路经Travel
有趣的思考题 Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第 ...
随机推荐
- MySQL 日志功能详解
MySQL日志分类 1:查询日志 :query log 2:慢查询日志:slow_query_log 查询执行时长超过指定时长的查询操作所记录日志 3:错误日志:error log ...
- 更新user的方法
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.forms import UserChangeForm ...
- 使用python操作Memcache、Redis、RabbitMQ、
Memcache 简述: Memcache是一套分布式的高速缓存系统,由LiveJournal的Brad Fitzpatrick开发,但目前被许多网站使用以提升网站的访问速度,尤其对于一些大型的.需要 ...
- angular 延迟更新方法
失去焦点后更新: <input ng-model="name" ng-model-options="{updateOn:'blur'}" />{{n ...
- HDU4759_Poker Shuffle
这是一个很好的题目,来自2013长春网赛. 题目的意思是给你2^N张扑克牌,每次洗牌前分别把从下开始数为奇数和偶数的牌分别拿出来放在一堆,两堆可以任意一个放在上面. 现在问你是否存在一种情况使得经过若 ...
- 【bzoj5206】[Jsoi2017]原力 根号分治+STL-map
题目描述 一个原力网络可以看成是一个可能存在重边但没有自环的无向图.每条边有一种属性和一个权值.属性可能是R.G.B三种当中的一种,代表这条边上原力的类型.权值是一个正整数,代表这条边上的原力强度.原 ...
- Java实现Websocket
Websocket介绍 在一个 WebSocket应用中, 服务器发布一个 WebSocket端点, 客户端使用这个端点的URI来连接服务器.建立连接之后,websocket协议是对称的;客户端和服务 ...
- 怎样搭建一个自有域名的 WORDPRESS 博客?
博客搭建并不复杂,只是过程有点繁琐,适合喜欢折腾的人,主要有下面几个步骤: 新建一个博客文件 购买域名(Domain Name) 注册一个主机空间(Web Host) 域名解析(DNSPod) 安装W ...
- 【刷题】BZOJ 1036 [ZJOI2008]树的统计Count
Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成 一些操作: I. CHANGE u t : 把结点u的权值改为t II. ...
- Codeforces Round #353 (Div. 2) A
弱到只会写div2的A题... 题面: A. Infinite Sequence time limit per test 1 second memory limit per test 256 mega ...