bzoj3258: 祕密任務 (+一百題感言?)

bzoj3258: 祕密任務
題意:n<=400個點,m<=4000條邊,可以在每條邊的起點或終點截斷這條邊。求截斷所有1到n最短路徑的最小花費,以及方案是否唯一。


題解

顯然是個最小割的模型。
按照題意跑一遍最短路,把不在最短路里的邊刪掉,其他的拆成兩條邊,流量分別賦起點和終點的費用,跑最大流,第一問結束。
有關最小割是否有唯一方案,參考了這個blog↓
[BZOJ 1797][BZOJ 3258]最小割的唯一性判定
跑一遍tarjan對於一條滿流邊。如果起點與終點分屬兩個scc,那麼它可以作爲原圖最小割的一條邊。而當起點與S所在scc相同,終點與T所在scc相同時,這條邊的選擇是唯一的。


代碼

說起來是時隔半年後第一次上3000B呢。
交完去喫飯,回來發現1A了,還是很開心的。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 5000
#define M 24006
#define ll long long
using namespace std;
int T,n,m,n_,a[N];
int to[M],hd[M],val[M],lk[N],cur[N];
char ch;
int q[N],h,t,dfn[N],low[N],tms;
bool inq[N];
ll dis[N],ans;
void add(int u,int v,int w,int loc)
{to[loc]=v,hd[loc]=lk[u],val[loc]=w,lk[u]=loc;}
void read(int &x)
{
    x=0;
    do ch=getchar();
    while(ch<'0'||ch>'9');
    do x=x*10+ch-'0',ch=getchar();
    while(ch>='0'&&ch<='9');
}
int u,v,w;
bool bfs()
{
    for(int i=1;i<=n_;i++)
    dis[i]=0,cur[i]=lk[i];
    h=0,t=dis[1]=1,q[0]=1;
    while(h<t)
    {
        u=q[h++];
        for(int i=lk[u];i;i=hd[i])
        if(val[i]&&!dis[to[i]])
        dis[q[t++]=to[i]]=dis[u]+1;
    }
    return dis[n];
}
ll dfs(int x,ll mx)
{
    if(x==n)return mx;
    ll ret=0,cst;
    for(int &i=cur[x];i;i=hd[i])
    if(val[i]&&dis[to[i]]==dis[x]+1)
    {
        cst=dfs(to[i],mx<val[i]?mx:val[i]);
        if(!cst)dis[to[i]]=0;
        else ret+=cst,mx-=cst,val[i]-=cst,val[i^1]+=cst; 
        if(!mx)break;
    }
    return ret;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++tms;
    inq[q[t++]=x]=1;
    for(int i=lk[x];i;i=hd[i])
    if(val[i])
    {
        if(!dfn[to[i]])
        {
            tarjan(to[i]);
            if(low[x]>low[to[i]])
            low[x]=low[to[i]];
        }
        else if(inq[to[i]]&&low[x]>dfn[to[i]])
        low[x]=dfn[to[i]];
    }
    if(dfn[x]==low[x])
    {
        while(q[--t]!=x)
        inq[q[t]]=0,cur[q[t]]=x;
        inq[x]=0,cur[x]=x;
    }
}
bool f;
int main()
{
    read(T);
    while(T--)
    {
        read(n),read(m);
        n_=n;
        for(int i=1;i<n;i++)
        read(a[i]);
        memset(lk,0,sizeof lk);
        for(int i=2;i<(m<<2);i+=2)
        {
            read(u),read(v),read(w);
            add(u,v,w,i);
            i+=2,add(v,u,w,i); 
        }
        memset(dis,0x3f,sizeof dis);
        h=dis[1]=0,t=q[0]=1;
        while(h!=t)
        {
            u=q[h++],inq[u]=0;
            if(h==N)h=0;
            for(int i=lk[u];i;i=hd[i])
            if(dis[to[i]]>dis[u]+val[i])
            {
                dis[to[i]]=dis[u]+val[i];
                if(!inq[to[i]])
                {
                    inq[q[t++]=to[i]]=1;
                    if(t==N)t=0;
                }
            }
        }
        memset(lk,0,sizeof lk);
        for(int i=2;i<(m<<2);i+=4)
        {
            u=to[i],v=to[i+2],w=val[i];
            if(dis[v]+w==dis[u])swap(u,v);
            if(dis[u]+w>dis[v])continue;
            n_++;
            if(u==n)continue;
            if(v==n)
            add(u,v,a[u],i),add(v,u,0,i|1);
            else
            add(u,n_,a[u],i),add(n_,u,0,i|1),
            add(n_,v,a[v],i+2),add(v,n_,0,i+3);
        }
        ans=0;
        while(bfs())ans+=dfs(1,0x3f3f3f3f);
        memset(dfn,0,sizeof dfn);t=0;
        for(int i=1;i<=n_;i++)
        if(!dfn[i])tarjan(i);
        f=1;
        for(int i=1;i<=n_;i++)
        for(int j=lk[i];j;j=hd[j])
        {
            u=cur[to[j]],v=cur[to[j^1]];
            if(val[j]||u==v||(j&1))
            continue;
            if(u!=cur[1])
            swap(u,v);
            if(u!=cur[1]||v!=cur[n])
            {f=0;break;}
        }
        if(f)printf("Yes ");
        else printf("No ");
        printf("%d\n",ans);
    }
}

題目無關

離在bzoj上交A+B過了13個月,離開權限也有九個半月了吧。現在才刷到100是因爲中間有半年純文化課,沒碰OI。不過中考結束,終於還是回來被碾壓了呢……
周圍oier的人員組成果然有變化,也意識到這個團隊/這種生活可能和自己一直想象的不太一樣。
以及,發現自己的代碼力倒退至一年前的暑假,與此同時,機房裏的其他人都在屠day榜……簡直可怕……
不過並沒有退路。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章