P2483-[模板]k短路/[SDOI2010]魔法猪学院【主席树,堆】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P2483


1|1题目大意

给出一个n个点m条边的一张带权有向图,求一个最大的k使得1n的前k短路径长度和不超过E

2n5000,1m2×105,1E107


1|2解题思路

我们先把从n出发的一棵反向最短路树跑出来,注意这里的最短路树是真的一棵树,我们从DAG中随便提一些边出来构成一棵树,以n为根。

然后考虑我们将其他的路径中不在树上的路径拿出来,那么这些边肯定满足前一条边的终点t肯定在后一条边起点s的子树中。

如果我们能确定这样一个有序的边集就相当于确定了这样一条唯一的路径。

然后考虑一条边(x,y,w)的边权视为disydisx+w,这样最终的路径长度就是dis1加上这些边的权值和。

然后考虑怎么去扩展我们的方案,假设我们现在的边集E的倒数第一个和最后一个分别是(x,y,w),(x,y,w)

那么我们有两种扩展方式:

  1. 在最后加一条边,这条边的起点一点得是yy的祖先。
  2. 替换最后一条边,这条边必须是起点是y或其祖先中恰好比当前这条边权大的边。

因为有重复边权,我们还是给每条边一个具体的顺序。

然后用堆维护最后两条边的状态就好了,主席树维护每个点其祖先的所有出边的一棵权值线段树。


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> #include<map> #define mp(x,y) make_pair(x,y) using namespace std; const int N=2e5+10,M=N<<5; const double eps=1e-8; struct edge{ int x,y; bool ban; double w; }e[N]; struct node{ int to,next,w; }a[N]; int n,m,tot,ans,ls[N],ban[N],fa[N],rt[N]; double f[N],k;bool v[N];map<int,int> u[N]; vector<int> T[N],ot[N]; priority_queue<pair<double,int> >_q; priority_queue<pair<double,pair<int,int> > >q; struct SegTree{ int cnt,w[M],ls[M],rs[M]; int Change(int x,int L,int R,int pos){ int p=++cnt;w[p]=w[x]+1; if(L==R)return p;int mid=(L+R)>>1; if(pos<=mid)ls[p]=Change(ls[x],L,mid,pos),rs[p]=rs[x]; else rs[p]=Change(rs[x],mid+1,R,pos),ls[p]=ls[x]; return p; } int Ask(int x,int L,int R,int k){ if(k>R||!w[x])return 0; if(L==R)return L;int mid=(L+R)>>1; if(k>mid)return Ask(rs[x],mid+1,R,k); int ans=Ask(ls[x],L,mid,k); if(!ans)ans=Ask(rs[x],mid+1,R,k); return ans; } }S; void addl(int x,int y,int w){ a[++tot].to=y; a[tot].next=ls[x]; ls[x]=tot;a[tot].w=w; return; } void dij(){ for(int i=1;i<n;i++)f[i]=1e100; _q.push(mp(0,n)); while(!_q.empty()){ int x=_q.top().second;_q.pop(); if(v[x])continue;v[x]=1; for(int i=ls[x];i;i=a[i].next){ int y=a[i].to; if(f[x]+e[a[i].w].w<f[y]){ f[y]=f[x]+e[a[i].w].w; ban[y]=a[i].w;fa[y]=x; _q.push(mp(-f[y],y)); } } } return; } bool cmp(edge x,edge y) {return x.w<y.w;} void dfs(int x){ for(int i=0;i<ot[x].size();i++) rt[x]=S.Change(rt[x],1,m,ot[x][i]); for(int i=0;i<T[x].size();i++) rt[T[x][i]]=rt[x],dfs(T[x][i]); return; } void solve(){ int x=S.Ask(rt[1],1,m,0); q.push(mp(-f[1]-e[x].w,mp(1,x))); while(!q.empty()){ double w=-q.top().first; int x=q.top().second.first; int y=q.top().second.second; q.pop(); if(k+eps<w){ans++,ans--;return;} k-=w;ans++; int p=S.Ask(rt[e[y].y],1,m,1); if(p)q.push(mp(-w-e[p].w,mp(e[y].y,p))); p=S.Ask(rt[x],1,m,y+1); if(p)q.push(mp(-w+e[y].w-e[p].w,mp(x,p))); } } int main() { // freopen("P2483_3.in","r",stdin); scanf("%d%d%lf",&n,&m,&k); for(int i=1;i<=m;i++){ scanf("%d%d%lf",&e[i].x,&e[i].y,&e[i].w); addl(e[i].y,e[i].x,i); } dij(); if(k<f[1])return puts("0")&0;k-=f[1];ans++; for(int i=1;i<n;i++)e[ban[i]].ban=1; for(int i=1;i<n;i++)T[fa[i]].push_back(i); for(int i=1;i<=m;i++) if(!e[i].ban&&e[i].x!=n) e[i].w=f[e[i].y]+e[i].w-f[e[i].x]; else swap(e[i],e[m]),m--,i--; sort(e+1,e+1+m,cmp); for(int i=1;i<=m;i++)ot[e[i].x].push_back(i); dfs(n);solve(); printf("%d\n",ans); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/16412427.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示