Codeforces Round #372 (Div. 2) D. Complete The Graph 好题+图论+思维+二分

将可改变的边长度先置为1,不断用dij跑最短路,若d[t]<L,增加可改变边的权值使该条最短路长度为L。直至跑完后d[t]==L结束。

注意原本的最短路已经小于L的情况,此时,最坏可能得跑m次dij,时间复杂度O(mmlogn),2168ms。

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
#define fst first
#define snd second
typedef long long ll;
typedef unsigned int uii;
typedef pair<ll,int> pli;
typedef pair<int,int> pii;
const ll inf=1e15;
const int maxn=1005;
const int maxm=10005;
int n,m,s,t,u,v;
set<pii> st;
ll w,d[maxn],L;
pii fa[maxn];
struct ee {
    int u,v,id;
    bool f;
    ll w;
    ee(){}
    ee(int u,int v,int id,bool f,ll w):u(u),v(v),id(id),f(f),w(w){}
};
vector<ee> vec[maxn];
priority_queue<pli,vector<pli>,greater<pli> > que;
void dij() {
    memset(fa,-1,sizeof fa);
    for (int i=0;i<n;++i)
        d[i]=inf;
    d[s]=0;
    que.push(pli(0,s));
    while (!que.empty()) {
        pli tp=que.top();
        que.pop();
        int u=tp.snd;
        ll td=tp.fst;
        if (td>d[u])
            continue;
        for (uii i=0;i<vec[u].size();++i) {
            int v=vec[u][i].v;
            ll c=vec[u][i].w;
            if (d[v]>d[u]+c) {
                d[v]=d[u]+c;
                fa[v]=pii(u,i);
                que.push(pli(d[v],v));
            }
        }
    }
}
bool fil(int u) {
    while (fa[u].fst!=-1) {
        ee &te=vec[fa[u].fst][fa[u].snd];
        if (te.f==true) {
            te.w+=L-d[t];
            vec[te.v][te.id].w+=L-d[t];
            return true;
        }
        u=fa[u].fst;
    }
    return false;
}
int main()
{
    scanf("%d%d%lld%d%d",&n,&m,&L,&s,&t);
    for (int i=0;i<m;++i) {
        scanf("%d%d%lld",&u,&v,&w);
        vec[u].push_back(ee(u,v,vec[v].size(),!w,max(1LL,w)));
        vec[v].push_back(ee(v,u,vec[u].size()-1,!w,max(1LL,w)));
    }
    while (true) {
        dij();
        if (!(d[t]<L&&fil(t)))
            break;
    }
    if (d[t]!=L)
        puts("NO");
    else {
        puts("YES");
        for (int i=0;i<n;++i)
            for (uii j=0;j<vec[i].size();++j) {
                int u=vec[i][j].u,v=vec[i][j].v;
                ll w=vec[i][j].w;
                if (st.find(pii(min(u,v),max(u,v)))==st.end()) {
                    printf("%d %d %lld\n",u,v,w);
                    st.insert(pii(min(u,v),max(u,v)));
                }
            }
    }
    return 0;
}

删掉不在当前 【可能的】 最短路上的可变边能使程序更快一点。见pre()函数。但若AB点间有许多可变边均在可能的最短路上,时间效率没有太高的优化,1185ms。

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
#define fst first
#define snd second
typedef long long ll;
typedef unsigned int uii;
typedef pair<ll,int> pli;
typedef pair<int,int> pii;
const ll inf=1e15;
const int maxn=1005;
const int maxm=10005;
int n,m,s,t,u,v;
set<pii> st;
ll w,d[maxn],L;
pii fa[maxn];
struct ee {
    int u,v,id;
    bool f;
    ll w;
    ee(){}
    ee(int u,int v,int id,bool f,ll w):u(u),v(v),id(id),f(f),w(w){}
};
vector<ee> vec[maxn];
priority_queue<pli,vector<pli>,greater<pli> > que;
void dij() {
    memset(fa,-1,sizeof fa);
    for (int i=0;i<n;++i)
        d[i]=inf;
    d[s]=0;
    que.push(pli(0,s));
    while (!que.empty()) {
        pli tp=que.top();
        que.pop();
        int u=tp.snd;
        ll td=tp.fst;
        if (td>d[u])
            continue;
        for (uii i=0;i<vec[u].size();++i) {
            int v=vec[u][i].v;
            ll c=vec[u][i].w;
            if (d[v]>d[u]+c) {
                d[v]=d[u]+c;
                fa[v]=pii(u,i);
                que.push(pli(d[v],v));
            }
        }
    }
}
bool fil(int u) {
    while (fa[u].fst!=-1) {
        ee &te=vec[fa[u].fst][fa[u].snd];
        if (te.f==true) {
            te.w+=L-d[t];
            vec[te.v][te.id].w+=L-d[t];
            return true;
        }
        u=fa[u].fst;
    }
    return false;
}
void pre() {
    dij();
    for (int i=0;i<n;++i)
        for (uii j=0;j<vec[i].size();++j) {
            int u=vec[i][j].u,v=vec[i][j].v;
            ll w=vec[i][j].w;
            if (vec[i][j].f&&max(d[v],d[u])!=min(d[v],d[u])+w)
                vec[i][j].w=inf;
        }
}
int main()
{
    scanf("%d%d%lld%d%d",&n,&m,&L,&s,&t);
    for (int i=0;i<m;++i) {
        scanf("%d%d%lld",&u,&v,&w);
        vec[u].push_back(ee(u,v,vec[v].size(),!w,max(1LL,w)));
        vec[v].push_back(ee(v,u,vec[u].size()-1,!w,max(1LL,w)));
    }
    pre();
    while (true) {
        dij();
        if (!(d[t]<L&&fil(t)))
            break;
    }
    if (d[t]!=L)
        puts("NO");
    else {
        puts("YES");
        for (int i=0;i<n;++i)
            for (uii j=0;j<vec[i].size();++j) {
                int u=vec[i][j].u,v=vec[i][j].v;
                ll w=vec[i][j].w;
                if (st.find(pii(min(u,v),max(u,v)))==st.end()) {
                    printf("%d %d %lld\n",u,v,w);
                    st.insert(pii(min(u,v),max(u,v)));
                }
            }
    }
    return 0;
}

删掉不在当前选定最短路上的可变边能使程序更快上许多。因为这里最短路上最多只有n-1条边。最多跑n-1次dij,时间复杂度O(nmlogn),342ms。

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
#define fst first
#define snd second
typedef long long ll;
typedef unsigned int uii;
typedef pair<ll,int> pli;
typedef pair<int,int> pii;
const ll inf=1e15;
const int maxn=1005;
const int maxm=10005;
int n,m,s,t,u,v,a,b;
bool vis[maxn][maxn];
set<pii> st;
ll w,d[maxn],L;
pii fa[maxn];
struct ee {
    int u,v,id;
    bool f;
    ll w;
    ee(){}
    ee(int u,int v,int id,bool f,ll w):u(u),v(v),id(id),f(f),w(w){}
};
vector<ee> vec[maxn];
priority_queue<pli,vector<pli>,greater<pli> > que;
void dij() {
    memset(fa,-1,sizeof fa);
    for (int i=0;i<n;++i)
        d[i]=inf;
    d[s]=0;
    que.push(pli(0,s));
    while (!que.empty()) {
        pli tp=que.top();
        que.pop();
        int u=tp.snd;
        ll td=tp.fst;
        if (td>d[u])
            continue;
        for (uii i=0;i<vec[u].size();++i) {
            int v=vec[u][i].v;
            ll c=vec[u][i].w;
            if (d[v]>d[u]+c) {
                d[v]=d[u]+c;
                if (fa[v].fst!=-1) {
                    a=fa[v].fst,b=fa[v].snd;
                    vis[a][b]=false;
                    vis[vec[a][b].v][vec[a][b].id]=false;
                }
                fa[v]=pii(u,i);
                vis[u][i]=true;
                vis[vec[u][i].v][vec[u][i].id]=true;
                que.push(pli(d[v],v));
            }
        }
    }
}
bool fil(int u) {
    while (fa[u].fst!=-1) {
        ee &te=vec[fa[u].fst][fa[u].snd];
        if (te.f==true) {
            te.w+=L-d[t];
            vec[te.v][te.id].w+=L-d[t];
            return true;
        }
        u=fa[u].fst;
    }
    return false;
}
void pre() {
    dij();
    for (int i=0;i<n;++i)
        for (uii j=0;j<vec[i].size();++j)
            if (vec[i][j].f&&!vis[i][j])
                vec[i][j].w=inf;
}
int main()
{
    scanf("%d%d%lld%d%d",&n,&m,&L,&s,&t);
    for (int i=0;i<m;++i) {
        scanf("%d%d%lld",&u,&v,&w);
        vec[u].push_back(ee(u,v,vec[v].size(),!w,max(1LL,w)));
        vec[v].push_back(ee(v,u,vec[u].size()-1,!w,max(1LL,w)));
    }
    pre();
    while (true) {
        dij();
        if (!(d[t]<L&&fil(t)))
            break;
    }
    if (d[t]!=L)
        puts("NO");
    else {
        puts("YES");
        for (int i=0;i<n;++i)
            for (uii j=0;j<vec[i].size();++j) {
                int u=vec[i][j].u,v=vec[i][j].v;
                ll w=vec[i][j].w;
                if (st.find(pii(min(u,v),max(u,v)))==st.end()) {
                    printf("%d %d %lld\n",u,v,w);
                    st.insert(pii(min(u,v),max(u,v)));
                }
            }
    }
    return 0;
}

题解里说有人在比赛时写了一个二分的代码,学习了一发发现有道理。

首先二分可变边的数目p,1-p置为1,p到k置为inf。找出需要置为1的最少可变边的数目。此部分复杂度O(mlognlogm)

得到p后,二分第p条需要改成的值。此部分复杂度O(mlognlogL)

总时间复杂度O(mlogn(logL+logm)),42ms

#include <bits/stdc++.h>
using namespace std;
#define fst first
#define snd second
typedef long long ll;
typedef pair<ll,int> pli;
typedef pair<int,int> pii;
typedef unsigned int uii;
const int maxn=1005;
const ll inf=1e15;
int n,m,s,t,u,v,tot,l,r,mid,res=-1,to;
ll L,w,d[maxn],tr;
bool vis[maxn][maxn];
vector<pli> vec[maxn];
vector<pii> det;
priority_queue<pli,vector<pli>,greater<pli> > que;
void dij() {
    for (int i=0;i<n;++i)
        d[i]=inf;
    d[s]=0;
    que.push(pli(0LL,s));
    while (!que.empty()) {
        int u=que.top().snd;
        ll td=que.top().fst;
        que.pop();
        if (td>d[u])
            continue;
        for (uii i=0;i<vec[u].size();++i) {
            int v=vec[u][i].snd;
            ll c=vec[u][i].fst;
            if (d[v]>d[u]+c) {
                d[v]=d[u]+c;
                que.push(pli(d[v],v));
            }
        }
    }
}
bool check(int mid) {
    for (int i=0;i<(mid<<1);++i)
        vec[det[i].fst][det[i].snd].fst=1;
    for (uii i=mid<<1;i<det.size();++i)
        vec[det[i].fst][det[i].snd].fst=inf;
    dij();
    if (d[t]>L)
        return false;
    return true;
}
ll c2(int mid) {
    if (res>=0) {
        vec[det[res<<1].fst][det[res<<1].snd].fst=mid;
        vec[det[(res<<1)|1].fst][det[(res<<1)|1].snd].fst=mid;
    }
    dij();
    return d[t];
}
int main()
{
    scanf("%d%d%lld%d%d",&n,&m,&L,&s,&t);
    for (int i=0;i<m;++i) {
        scanf("%d%d%lld",&u,&v,&w);
        vec[u].push_back(pli(w,v));
        vec[v].push_back(pli(w,u));
        if (!w) {
            det.push_back(pii(u,vec[u].size()-1));
            det.push_back(pii(v,vec[v].size()-1));
        }
    }
    tot=det.size()>>1;
    l=0,r=tot;
    while (l<=r) {
        mid=(l+r)>>1;
        if (check(mid)) {
            res=mid;
            r=mid-1;
        } else
            l=mid+1;
    }
    if (res==-1)
        puts("NO");
    else {
        --res;
        l=0,r=L;
        while (l<=r) {
            mid=(l+r)>>1;
            tr=c2(mid);
            if (tr==L)
                break;
            else if (tr<L)
                l=mid+1;
            else
                r=mid-1;
        }
        if (l>r)
            puts("NO");
        else {
            puts("YES");
            for (int i=0;i<n;++i)
                for (uii j=0;j<vec[i].size();++j) {
                    to=vec[i][j].snd;
                    if (!vis[i][to]) {
                        vis[i][to]=vis[to][i]=true;
                        printf("%d %d %lld\n",i,to,vec[i][j].fst);
                    }
                }
        }
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值