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

来源:互联网 发布:如何连接linux服务器 编辑:程序博客网 时间:2024/06/06 17:40

将可改变的边长度先置为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 secondtypedef 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 secondtypedef 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 secondtypedef 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 secondtypedef 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
原创粉丝点击