【XSY2278】【HDU5669】the Red Sun(线段树+dijkstra)
\(Description\)
给定一张 \(N\) 个点的图, 点的标号为 \(1\) 到 \(n\) . 我们进行 \(M\) 次连边, 每次连边可以描述为 \(a\) \(b\) \(c\) \(d\) \(w:\)
for i = a to b do
for j = c to d do
Add_Edge(i,j,w)
\(Add_Edge(i,j,w)\) 表示从点 \(i\) 向点 \(j\) 连一条费用为 \(w\) 的双向边.
求点 \(1\) 到点 \(n\) 的最小花费.
为了降低难度, 我们有 \(K\) 次机会可以消除某条边的花费.
\(Input\)
第一行一个数 \(T\) , 表示测试数据组数. 出于某种原因, \(T=1 .\)
第二行三个数 \(N,M,K\) .
接下来 \(M\) 行, 每行 \(5\) 个数 \(a,b,c,d,w\) , 意义如题目描述所示.
\(Output\)
一行一个数, 为最小的花费.
如果点 \(1\) 与点 \(n\) 不连通, 输出 "Yww is our red sun!" .
\(Sample Input\)
1
5 3 0
1 2 4 5 42
5 5 4 4 468
1 1 3 3 335
\(Sample Output\)
42
\(HINT\)
\(T=1,1≤N≤5×10^4,1≤M≤10^4,0≤K≤10,1≤w≤10^3\)
\(Source\)
练习题 树4-1-线段树
思路
思路可以参考【XSY2434】【CF787D】遗产(也是我自己写的)
基本上思路一致
也是一个最短路的题目,使用线段树优化
对于每两个\([a,b]\)和\([c,d]\)区间间的连边,我们设一个中间点\(x\),将\([a,b]\)连向\(x\),再由\(x\)连向\([c,d]\)
我们现在开始考虑怎么求最短路
我们都知道要使用\(dijkstra\)
但是多了一个限制条件我们有 \(K\) 次机会可以消除某条边的花费
于是我们显然想到了\(dp\)
设\(dis[i][j]\)表示到达\(i\)节点,用了\(j\)次机会的最小花费
于是我们可以得到状转方程:
\(\begin{cases}dis[now.x][v]=now.cost+val[i]&dis[now.x][v]>now.cost+val[i]\\dis[now.x+1][v]=now.cost&now.x<k且dis[now.x+1][v]>now.cost\end{cases}\)
我们就可以快乐地实现最短路啦
代码
#include<bits/stdc++.h>
using namespace std;
const int N=400010,M=2000010;
int n,m,k,rt1,rt2,cnt=0;
int lc1[N],rc1[N],lc2[N],rc2[N];
int dis[15][N];
bool vis[15][N];
int w;
int to[M<<1],nxt[M<<1],val[M<<1],head[N];
int nodetot=0;
struct edge
{
int u,cost,x;
bool operator < (const edge & e)const
{
return cost>e.cost;
}
};
void add(int u,int v,int wi)
{
to[++cnt]=v;
val[cnt]=wi;
nxt[cnt]=head[u];
head[u]=cnt;
}
int build1(int l,int r)
{
if(l==r)return l;
int now=++nodetot;
int mid=(l+r)>>1;
lc1[now-n]=build1(l,mid);
add(lc1[now-n],now,0);
rc1[now-n]=build1(mid+1,r);
add(rc1[now-n],now,0);
return now;
}
int build2(int l,int r)
{
if(l==r)return l;
int now=++nodetot;
int mid=(l+r)>>1;
lc2[now-n]=build2(l,mid);
add(now,lc2[now-n],0);
rc2[now-n]=build2(mid+1,r);
add(now,rc2[now-n],0);
return now;
}
void modify1(int k,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
{
add(k,nodetot,w);
return ;
}
int mid=(l+r)>>1;
if(ql<=mid)modify1(lc1[k-n],l,mid,ql,qr);
if(qr>mid)modify1(rc1[k-n],mid+1,r,ql,qr);
}
void modify2(int k,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
{
add(nodetot,k,0);
return ;
}
int mid=(l+r)>>1;
if(ql<=mid)modify2(lc2[k-n],l,mid,ql,qr);
if(qr>mid)modify2(rc2[k-n],mid+1,r,ql,qr);
}
priority_queue<edge>qu;
void dijkstra()
{
memset(dis,127,sizeof(dis));
dis[0][1]=0;
qu.push((edge){1,0,0});
edge now;
while(!qu.empty())
{
now=qu.top();
qu.pop();
if(vis[now.x][now.u])continue;
vis[now.x][now.u]=1;
for(int i=head[now.u];i;i=nxt[i])
{
int v=to[i];
if(dis[now.x][v]>now.cost+val[i]&&!vis[now.x][v])
{
dis[now.x][v]=now.cost+val[i];
qu.push((edge){v,dis[now.x][v],now.x});
}
if(now.x<k&&!vis[now.x+1][v]&&dis[now.x+1][v]>now.cost)
{
dis[now.x+1][v]=now.cost;
qu.push((edge){v,dis[now.x+1][v],now.x+1});
}
}
}
}
int main()
{
scanf("%d",&n);
scanf("%d %d %d",&n,&m,&k);
int a,b,c,d;
nodetot=n;
rt1=build1(1,n);
rt2=build2(1,n);
while(m--)
{
scanf("%d %d %d %d %d",&a,&b,&c,&d,&w);
nodetot++;//中间点
modify1(rt1,1,n,a,b);modify2(rt2,1,n,c,d);
nodetot++;//中间点
modify1(rt1,1,n,c,d);modify2(rt2,1,n,a,b);
}
dijkstra();
int ans=dis[0][0];
for(int i=0;i<=k;i++)ans=min(ans,dis[i][n]);
if(ans==dis[0][0])puts("Yww is our red sun!");
else printf("%d",ans);
return 0;
}