zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:城市游戏(图论+DP)

    题目传送门(内部题109)


    输入格式

      第一行,两个整数$n,m$。
      接下来$m$行,每行三个整数$u,v,l$,描述了一条道路连接的两个路口的编号以及道路的长度。


    输出格式

      输出一行一个整数,为所求的答案。若小$A$不能到$n$达号点,输出$-1$。


    样例

    样例输入:

    4 4
    1 2 1
    2 3 1
    3 4 1
    1 4 1

    样例输出:

    3


    数据范围与提示

      对于$10\%$的数据,满足$nleqslant 200,mleqslant 1,000$
      对于$70\%$的数据,满足$nleqslant 1,000,mleqslant 2,000$
      对于$40\%$的数据,满足$nleqslant 3,000,mleqslant 10,000$
      对于$100\%$的数据,满足$1leqslant nleqslant 100,000,1leqslant mleqslant 200,000$,道路长度为不超过$10^9$的正整数。


    题解

    首先,小$B$一定是要“封掉”$1sim n$的最短路上的一条边。

    设$dp[i]$小$B$还没有用过“封路”时从$i$到$n$的最短路的长度,$d(i,j)$表示小$B$封掉$(i,j)$这条边之后小$A$从$i$到$n$的最短路的长度,$dis[i]$表示从$n$到$i$的最短路的长度。

    假设现在小$A$在点$i$,且要走$(i,j)$这条边,那么$f[i]=max(f[j]+l(i,j),d(i,j))$,这也就是一个带环的动态规划,可以利用类似$Dijkstra$的思路解决。

    现在考虑如何求出$d(i,j)$。

    有一个原题:$USACO$《安全路径》。

    思路就是先构建出一棵以$n$为根的最短路树,那么只有$(i,j)$在这棵最短路树上$d(i,j)$才有意义。

    不妨设删掉的这条边为$(u,v)$,父亲是$u$,儿子是$v$,那么在删掉这条边之后树分成了两个联通块。

    那么我们需要找一条边$(a,b)$满足$a$在$v$的联通块内,而$b$在$u$的联通块内,那么这条路经的长度就是$dis[b]+l(a,b)+(dis[a]-dis[v])=(dis[b]+dis[a]+l(a,b))-dis[v]$。

    对于上式,不妨设$dis[b]+dis[a]+l(a,b)=p(a,b)$,则想办法求的$p(a,b)$的最小值且满足$a$在$v$的子树中且$b$不在。

    枚举$v$显然不可行,考虑枚举$(a,b)$,看它能对哪些$v$做贡献。

    还可以发现$v$一定在$asim b$的树链上,且不能是$a$和$b$的$lca$。

    暴力是不可行的,但是可以按$p(a,b)$排序,那么赋值过一次就不能再被赋值了,可以用并查集维护。

    时间复杂度:$Theta(omega m+mlog n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    struct rec{int nxt,to,w;bool is;}e[2][400002];
    int head[2][100001],cnt[]={1,1},sz;
    int n,m;
    int pre[100001],fa[100001][21],depth[100001];
    long long dis[100001],dp[100001],ans[100001];
    bool vis[100001],is[400002];
    priority_queue<pair<long long,int>,vector<pair<long long,int>>,greater<pair<long long,int>>>q;
    bool cmp(rec a,rec b){return a.w<b.w;}
    void add(bool id,int x,int y,int w)
    {
    	e[id][++cnt[id]].nxt=head[id][x];
    	e[id][cnt[id]].to=y;
    	e[id][cnt[id]].w=w;
    	head[id][x]=cnt[id];
    }
    void Dij()
    {
    	q.push(make_pair(0,n));
    	while(q.size())
    	{
    		int x=q.top().second;q.pop();
    		if(vis[x])continue;vis[x]=1;
    		for(int i=head[0][x];i;i=e[0][i].nxt)
    		{
    			if(dis[e[0][i].to]>dis[x]+e[0][i].w)
    			{
    				dis[e[0][i].to]=dis[x]+e[0][i].w;
    				pre[e[0][i].to]=i;
    				q.push(make_pair(dis[e[0][i].to],e[0][i].to));
    			}
    		}
    	}
    }
    void dfs(int x)
    {
    	for(int i=head[1][x];i;i=e[1][i].nxt)
    	{
    		fa[e[1][i].to][0]=x;
    		depth[e[1][i].to]=depth[x]+1;
    		for(int j=1;j<=20;j++)
    			fa[e[1][i].to][j]=fa[fa[e[1][i].to][j-1]][j-1];
    		dfs(e[1][i].to);
    	}
    }
    int LCA(int x,int y)
    {
    	if(depth[x]>depth[y])swap(x,y);
    	for(int i=20;i>=0;i--)
    		if(depth[fa[y][i]]>=depth[x])
    			y=fa[y][i];
    	if(x==y)return x;
    	for(int i=20;i>=0;i--)
    		if(fa[x][i]!=fa[y][i])
    		{
    			x=fa[x][i];
    			y=fa[y][i];
    		}
    	return fa[x][0];
    }
    void DIJ()
    {
    	memset(vis,0,sizeof(vis));
    	q.push(make_pair(0,n));
    	while(q.size())
    	{
    		int x=q.top().second;q.pop();
    		if(vis[x])continue;vis[x]=1;
    		for(int i=head[0][x];i;i=e[0][i].nxt)
    		{
    			if(ans[e[0][i].to]>max(ans[x]+e[0][i].w,is[i]?dp[e[0][i].to]:dis[e[0][i].to]))
    			{
    				ans[e[0][i].to]=max(ans[x]+e[0][i].w,is[i]?dp[e[0][i].to]:dis[e[0][i].to]);
    				q.push(make_pair(ans[e[0][i].to],e[0][i].to));
    			}
    		}
    	}
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<n;i++)dis[i]=ans[i]=dp[i]=0x3f3f3f3f3f3f3f3f;
    	for(register int i=1;i<=m;i++)
    	{
    		int u,v,l;
    		scanf("%d%d%d",&u,&v,&l);
    		add(0,u,v,l);add(0,v,u,l);
    	}
    	Dij();
    	for(register int i=1;i<n;i++)
    	{
    		add(1,e[0][pre[i]^1].to,i,0);
    		is[pre[i]]=is[pre[i]^1]=1;
    	}
    	dfs(n);
    	for(register int i=2;i<=cnt[0];i+=2)
    	{
    		if(is[i])continue;
    		int x=e[0][i].to;
    		int y=e[0][i^1].to;
    		int lca=LCA(x,y);
    		long long val=dis[x]+dis[y]+e[0][i].w;
    		while(x!=lca)
    		{
    			dp[x]=min(dp[x],val-dis[x]);
    			x=fa[x][0];
    		}
    		while(y!=lca)
    		{
    			dp[y]=min(dp[y],val-dis[y]);
    			y=fa[y][0];
    		}
    	}
    	DIJ();
    	if(ans[1]==0x3f3f3f3f3f3f3f3f)puts("-1");
    	else printf("%lld",ans[1]);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    单击按钮左键弹起菜单
    高亮选中MEMO某一行
    DelphiTXT文档编辑器
    桌面名人名言
    判断richtextbox选中的是否为图片
    数组
    解决Linux下IDEA无法使用ibus输入法的问题和tip乱码
    Spring实现AOP的多种方式
    java术语(PO/POJO/VO/BO/DAO/DTO)
    idea intellij对Spring进行单元测试
  • 原文地址:https://www.cnblogs.com/wzc521/p/11776214.html
Copyright © 2011-2022 走看看