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榜……簡直可怕……
不過並沒有退路。