【USACO JAN 09】安全路径(Dijkstra+线段树合并)

传送门

安全路径

I think

    题意:给出一张由点1到各点最短路径唯一的图,求点1到各点不经过最短路径的最后一条边的最短路径长度。
    算法:Dijkstra+线段树合并
    思路:构造最短路径树,对于每一个点i,其答案路径长是i子树内一点e到i子树外的非i父亲节点j的路径长,加点e、j到点1的距离,减去点i到点1的距离,即 dis[1][e]+dis[1][j]+dis[j][e]dis[1][i] 。那么我们对于每个节点e就建一颗权值线段树维护 dis[1][e]+dis[1][j]+dis[j][e] 好了,遍历到一个点时先合并子节点权值线段树,更新权值线段树,删除以自己为lca的路径(子树合并之后线该路径两端即在同一颗树上了),计算该点的答案路径长。

Code

#include<queue>
#include<vector>
#include<cstdio>
#include<iostream>
#define s second
using namespace std;
typedef pair<int,int> pii;
const int sn = 100000+5;
const int sm = 200000+5;
const int sz = sn*20;

int n,m,tot,ya,yb,lim,cnt;
int dis[sn],ex[sn],anc[sn],ans[sn];//dijkstra&并查集
int to[sm<<1],nxt[sm<<1],w[sm<<1],hd[sn];//处理图的边
int Rt[sn],Ls[sz],Rs[sz],Val[sz],stk[sz],tp;//处理权值线段树
int ff[sn],dep[sn],Fa[sn][18];//处理Lca
priority_queue<pii,vector<pii>,greater<pii> >q;
vector<int>Lc[sn];

template <typename T> void read(T &x) {
    char ch=getchar();x=0;int f=1;
    while(ch>'9'||ch<'0') { if(ch=='-')f=-1; ch=getchar(); } 
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    x*=f;
}
void Swap(int &x,int &y) { int t=x;x=y;y=t; }
int Max(int x,int y) { return x>y?x:y; }
void Dijkstra(int st) {
    for(int i=1;i<=n;++i) dis[i]=lim,anc[i]=i;//一并初始化并查集
    dis[st]=0,q.push(make_pair(dis[st],st));
    while(!q.empty()) {
        pii t=q.top();q.pop();
        if(ex[t.s])continue;//别忘了一个点只能入队一次
        ex[t.s]=1;
        for(int i=hd[t.s];i;i=nxt[i])
            if(!ex[to[i]]&&dis[to[i]]>dis[t.s]+w[i]) {
                dis[to[i]]=dis[t.s]+w[i],ff[to[i]]=t.s;
                q.push(make_pair(dis[to[i]],to[i]));
            }
    }
}
int find(int x) {
    if(anc[x]!=x) anc[x]=find(anc[x]);
    return anc[x];
}
void del(int &x) {
    stk[++tp]=x,Ls[x]=Rs[x]=Val[x]=0,x=0;
}
void Insert(int &rt,int l,int r,int p,int val) {
    if(!rt) rt=(tp?stk[tp--]:++tot);
    Val[rt]+=val;
    if(l==r) { if(!Val[rt])del(rt); return; }
    int m=(l+r)>>1;
    if(p<=m) Insert(Ls[rt],l,m,p,val);
    else Insert(Rs[rt],m+1,r,p,val);
    if(!Ls[rt]&&!Rs[rt]&&!Val[rt]) del(rt);
}
int Query(int rt,int l,int r) {
    if(l==r)return l;
    int m=(l+r)>>1;
    if(Val[Ls[rt]]) return Query(Ls[rt],l,m);
    else return Query(Rs[rt],m+1,r);
}
int Merge(int x,int &y) {
    if(!(x*y))return x+y;
    Val[x]+=Val[y];
    Ls[x] = Merge(Ls[x],Ls[y]);
    Rs[x] = Merge(Rs[x],Rs[y]);
    del(y);
    return x;
}
int lca(int u,int v) {
    if(dep[u]!=dep[v]) {
        if(dep[u]<dep[v]) Swap(u,v);
        for(int i=17;i>=0;--i)
            if(dep[Fa[u][i]]>dep[v]) 
                u=Fa[u][i];
        u=Fa[u][0];
    }
    if(u==v)return u;
    for(int i=17;i>=0;--i)
        if(Fa[u][i]!=Fa[v][i])
            u=Fa[u][i],v=Fa[v][i];
    return Fa[u][0];
}
void Dfsa(int x,int fa) {
    dep[x]=dep[fa]+1,Fa[x][0]=fa;
    for(int i=1;i<=17;++i)
        if(Fa[x][i-1])Fa[x][i]=Fa[Fa[x][i-1]][i-1];
        else break;
    for(int i=hd[x];i;i=nxt[i])
        if(to[i]!=fa&&ff[to[i]]==x) 
            Dfsa(to[i],x);
}
void Dfsb(int x,int fa) {
    ya=find(x);
    for(int i=hd[x];i;i=nxt[i]) {
        if(to[i]==fa||ff[to[i]]!=x)continue;
        Dfsb(to[i],x);
        yb=find(to[i]);anc[yb]=ya;
        Rt[x]=Merge(Rt[x],Rt[to[i]]);
    }
    for(int i=hd[x];i;i=nxt[i])//更新权值线段树-加点
        if(to[i]!=fa&&ya!=find(to[i])) { 
            Insert(Rt[x],1,lim,dis[to[i]]+w[i]+dis[x],1);
            Lc[lca(x,to[i])].push_back(dis[to[i]]+w[i]+dis[x]);
        }
    for(int i=0;i<Lc[x].size();++i)//更新权值线段树-删点
        Insert(Rt[x],1,lim,Lc[x][i],-1);
    int tmp=Query(Rt[x],1,lim);
    ans[x]=Val[Rt[x]]?tmp-dis[x]:-1;
}
int main() {
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    read(n),read(m);
    for(int i=1,a,b,c;i<=m;++i) {
        read(a),read(b),read(c),lim=Max(c,lim);
        to[++tot]=b,nxt[tot]=hd[a],hd[a]=tot,w[tot]=c;
        to[++tot]=a,nxt[tot]=hd[b],hd[b]=tot,w[tot]=c;
    }
    lim*=n<<1;
    tot=0,Dijkstra(1);
    tot=0,Dfsa(1,0);
    Dfsb(1,0);
    for(int i=1;i<n;++i)
        printf("%d\n",ans[i+1]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是P4087 [USACO17DEC]Milk Measurement的c++代码: ```c++ #include<bits/stdc++.h> using namespace std; int n,d,i,x,minn=1e9,maxn=-1e9,sum=7;//注意sum要初始化为7,因为一开始有三个人挤奶! map<int,int> mp; struct node{ int day,milk,id;//day表示某一天,milk表示这一天的产奶量,id表示这头牛的编号 }a[100010]; bool cmp(node x,node y){ return x.day<y.day; } int main(){ scanf("%d%d",&n,&d); for(i=1;i<=n;i++){ scanf("%d%d%d",&a[i].day,&a[i].id,&a[i].milk); minn=min(minn,a[i].id);//记录最小的牛的编号 maxn=max(maxn,a[i].id);//记录最大的牛的编号 } sort(a+1,a+n+1,cmp);//排序 for(i=1;i<=n;i++){ int p=a[i].id; mp[p]+=a[i].milk;//记录每头牛产奶总量 if(mp[p]-a[i].milk>=mp[minn]&&mp[p]>=mp[minn]){//如果这头牛的产奶总量减去这一天的产奶量后等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 sum--; } if(mp[p]>=mp[maxn]&&mp[p]-a[i].milk<mp[maxn]){//如果这头牛的产奶总量大于等于最大产奶量且这头牛的产奶总量减去这一天的产奶量小于最大产奶量 sum++; } if(mp[p]-a[i].milk<mp[maxn]&&mp[p]>=mp[maxn]){//如果这头牛的产奶总量减去这一天的产奶量小于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]-mp[p]+a[i].milk>0)sum++; } mp[p]-=a[i].milk;//减去这一天的产奶量 if(i==n||a[i].day!=a[i+1].day){//如果到了新的一天或者到了最后一天 if(mp[maxn]!=mp[a[i].id]&&mp[a[i].id]>=mp[maxn])sum++;//如果这头牛的产奶总量不等于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]==mp[a[i].id]){//如果这头牛的产奶总量等于最大产奶量 if(a[i].id==maxn)sum+=0;//如果这头牛就是最大产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } if(mp[minn]!=mp[a[i].id]&&mp[a[i].id]>=mp[minn])sum++;//如果这头牛的产奶总量不等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 if(mp[minn]==mp[a[i].id]){ if(a[i].id==minn)sum+=0;//如果这头牛就是最小产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } } } printf("%d\n",sum); return 0; } ``` 该题的解题思路是模拟,需要注意细节问题。我们可以首先将输入的数据按天数排序,然后模拟每一天挤奶的情况,并根据题目要求进行计数即可。具体细节请见代码注释。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值