3882: [Wc2015]K小割
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 79 Solved: 50
[ Submit][ Status][ Discuss]
Description
Input
Output
Sample Input
1 2 3
2 3 4
1 3 5
Sample Output
9
12
-1
HINT
Source
PS:数据范围
题解:最小割+优先队列
这道题要想A掉需要写三个部分分。
part1:暴力枚举每条边割不割,保留所有合法的割,然后从小到大排序输出
时间复杂度O(2^m*n),可以过前两个点,因为前两个点的K较大,所有不能与下面的part3合并
part2:优先队列
7-14这几个点很有规律,对于除s,t外的每个点都分别有且仅有一条边与s,t相连。那么对于每一个点都必须至少割掉其中的一条边。设两条边的权值为a,b(a<b),那么对于每个点的选择有{a,b,a+b} 三种,我们可以通过对于每个点选择的升级/降级,来达到不同的状态。
我们将点以(b-a)为第一关键字(从第一级升到第二级的代价),a为第二关键字排序(从第二级升到第三级的代价)。
对于每个状态我们有三种选择
(1)将当前点的状态升级
(2)当前点保持不变,将该点的后一个点升级,并将当前点改为他后面的点。
(3)将当前点降级,将该点的后一个点升级,并将当前点改为他后面的点(需要注意的是在降级的时候只能是从b->a,不能是从a+b->b,否则会出现重复的状态,因为a+b->b的改变其实可以转化成选择(2))
总之在更新状态的时候需要时刻注意,不要更新到之前已经得到的状态,如果开始后一个点的升级,那么当前点就不能再改变了。
part3:最小割
剩下的部分分可以统一用这种方法解决。
我们先用最大流得到最小割,然后考虑怎么在最小割的基础上得到次小割。
次小割有两种产生的方式:
(1)强制割集中的某条边不选,然后求此时的最小割
(2)选择不再割集中的某条边,然后割掉。
对于第(2)种产生方式,必然是选择不在割集中的边权最小的边来更新答案。那么我们如何确定割集呢?从s开始走剩余流量不为0的边,将所有能遍历到的点打访问标记,如果一条边的两个端点x[i]有标记,y[i]没有标记,那么该边在最小割的割集中。
第一种情况,对于每一条边来说,如果这条边不割,那么s->x[i],y[i]->t都其中一个必须不连通,我们可以在做完最小割的残量网络上对于s->x[i],y[i]->t分别求最小割,然后从中选取较小的更新答案。
处理完(1)(2)两种产生方式后,得到的答案就是针对当前割集的次小割。
然后我们需要用类似k短路的方式进行搜索,每次可以强制产生次小割的边选与不选,进而得到更多的割集,依次取出前k个就是答案。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#define N 150003
#define M 3003
#define LL long long
using namespace std;
const LL inf=1e13;
struct data{
int now,opt; LL val;
bool operator <(data a) const{
return a.val<val;
}
};
struct node{
LL a,b;
}e[N];
int n,m,s,t,k,x[N],y[N],c[N],vis[N],fa[N],cnt,mark[20];
int point[N],v[N],nxt[N],num[N],tot;
LL ans[N*10];
int cmp(node a,node b)
{
return a.a<b.a||a.a==b.a&&a.b<b.b;
}
void add(int x,int y,int z)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; num[tot]=z;
}
bool check()
{
memset(mark,0,sizeof(mark));
queue<int> p; p.push(s); mark[s]=1;
while (!p.empty()) {
int now=p.front(); p.pop();
for (int i=point[now];i;i=nxt[i]) {
if (vis[num[i]]||mark[v[i]]) continue;
mark[v[i]]=1;
p.push(v[i]);
}
}
return !mark[t];
}
void dfs(int x,LL sum)
{
if (x==m+1) {
if (check())
ans[++ans[0]]=sum;
return;
}
vis[x]=1; dfs(x+1,sum+c[x]);
vis[x]=0; dfs(x+1,sum);
}
struct graph{
#define N1 53
int tot,point[N1],deep[N1],num[N1],cur[N1],mark[M];
int nxt[M],v[M],last[M]; LL remain[M];
void clear() {
tot=-1;
memset(point,-1,sizeof(point));
memset(num,0,sizeof(num));
}
void add(int x,int y,LL z)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z;
tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
// cout<<x<<" "<<y<<" "<<z<<endl;
}
LL addflow(int s,int t)
{
LL ans=inf; int now=t;
while (now!=s) {
ans=min(ans,remain[last[now]]);
now=v[last[now]^1];
}
now=t;
while (now!=s) {
remain[last[now]]-=ans;
remain[last[now]^1]+=ans;
now=v[last[now]^1];
}
return ans;
}
void bfs(int s,int t)
{
for (int i=1;i<=n;i++) deep[i]=n;
queue<int> p; p.push(t); deep[t]=0;
while (!p.empty()) {
int now=p.front(); p.pop();
for (int i=point[now];i!=-1;i=nxt[i])
if (deep[v[i]]==n&&remain[i^1])
deep[v[i]]=deep[now]+1,p.push(v[i]);
}
}
LL isap(int s,int t)
{
LL ans=0; int now=s;
bfs(s,t);
memset(num,0,sizeof(num));
for (int i=1;i<=n;i++) num[deep[i]]++;
for (int i=1;i<=n;i++) cur[i]=point[i];
while (deep[s]<n) {
if (now==t) {
ans+=addflow(s,t);
now=s;
}
bool pd=false;
for (int i=cur[now];i!=-1;i=nxt[i])
if (deep[v[i]]+1==deep[now]&&remain[i]) {
cur[now]=i;
last[v[i]]=i;
pd=true;
now=v[i];
break;
}
if (!pd) {
int minn=n;
for (int i=point[now];i!=-1;i=nxt[i])
if (remain[i]) minn=min(minn,deep[v[i]]);
if (!--num[deep[now]]) break;
num[deep[now]=minn+1]++;
cur[now]=point[now];
if (now!=s) now=v[last[now]^1];
}
}
return ans;
}
void dfs(int x)
{
vis[x]=1;
for (int i=point[x];i!=-1;i=nxt[i])
if (!vis[v[i]]&&remain[i]) dfs(v[i]);
}
void check() {
memset(mark,0,sizeof(mark));
memset(vis,0,sizeof(vis));
dfs(s);
for (int i=1;i<=m;i++)
if (vis[x[i]]&&!vis[y[i]]) mark[i]=1;
}
}T,t1,t2;
struct qu
{
bool able[M],uab[M],pds[M],pdt[M];
LL mins[53],mint[53],val;
int now;
bool operator <(qu a) const {
return a.val<val;
}
void build(){
t1=T; val=0;
for (int i=1;i<=m;i++) {
int u1=2*(i-1); int u2=u1+1;
if (able[i]) val+=(LL)c[i],t1.remain[u1]=0,t1.remain[u2]=0;
else if (uab[i]) t1.remain[u1]=inf,t1.remain[u2]=0;
}
//for (int i=1;i<=m;i++) cout<<able[i]<<" ";
//cout<<endl;
//for (int i=1;i<=m;i++) cout<<uab[i]<<" ";
//cout<<endl;
val+=t1.isap(s,t); t1.check();
// for (int i=1;i<=m;i++) cout<<t1.mark[i]<<" "; cout<<endl;
LL ansx; ansx=inf; now=0;
memset(pds,0,sizeof(pds)); memset(pdt,0,sizeof(pdt));
for (int i=1;i<=m;i++) {
if (able[i]||uab[i]) continue;
if (t1.mark[i]) {
if (!pds[x[i]]) {
pds[x[i]]=1,t2=t1;
// for (int j=1;j<=n;j++)
//for (int k=t2.point[j];k!=-1;k=t2.nxt[k]) cout<<t2.remain[k]<<endl;
mins[x[i]]=t2.isap(s,x[i]);
}
if (!pdt[y[i]]) pdt[y[i]]=1,t2=t1,mint[y[i]]=t2.isap(y[i],t);
if (ansx>mins[x[i]]) ansx=mins[x[i]],now=i;
if (ansx>mint[y[i]]) ansx=mint[y[i]],now=i;
}
else if (c[i]<ansx) ansx=c[i],now=i;
}
val+=ansx;
}
}a,cur,cur1;
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d%d%d%d%d",&n,&m,&s,&t,&k);
if (n<=10&&m<=20) {
cnt=0;
for (int i=1;i<=m;i++)
scanf("%d%d%d",&x[i],&y[i],&c[i]),add(x[i],y[i],i);
dfs(1,0); cnt=ans[0];
sort(ans+1,ans+cnt+1);
//for (int i=1;i<=cnt;i++) cout<<ans[i]<<" ";
//cout<<endl;
for (int i=1;i<=min(cnt,k);i++) printf("%lld\n",ans[i]);
if (k>cnt) printf("-1\n");
return 0;
} else
if (m==2*n-4) {
for (int i=1;i<=m;i++) {
int x,y,z; scanf("%d%d%d",&x,&y,&z);
if (x>y) swap(x,y);
if (x==1) e[y-1].a=(LL)z;
if (y==n) e[x-1].b=(LL)z;
}
LL ans=0;
for (int i=1;i<=n;i++) {
if (e[i].a>e[i].b) swap(e[i].a,e[i].b);
ans+=e[i].a;
LL t=e[i].a;
e[i].a=e[i].b-e[i].a;
e[i].b=t;
}
sort(e+1,e+n-1,cmp);
printf("%lld\n",ans); k--;
//for (int i=1;i<=n;i++) cout<<e[i].a<<" "<<e[i].b<<endl;
data cur; cur.now=1; cur.opt=1; cur.val=ans+e[1].a;
priority_queue<data> p; p.push(cur);
int size=0;
while (!p.empty()) {
cur=p.top(); p.pop();
printf("%lld\n",cur.val); size++;
if (size==k) break;
data nt; nt=cur;
if (nt.opt<2) {
nt.opt++;
nt.val+=e[nt.now].b;
p.push(nt);
}
nt=cur;
if (nt.now!=n-2) {
nt.now++;
nt.opt=1;
nt.val+=e[nt.now].a;
p.push(nt);
}
nt=cur;
if (nt.opt==1&&nt.now<n-2) {
nt.val-=e[nt.now].a;
nt.val+=e[nt.now+1].a;
nt.opt=1;
nt.now++;
p.push(nt);
}
}
if (k>size)printf("-1\n");
return 0;
} else
{
T.clear();
for (int i=1;i<=m;i++) {
scanf("%d%d%d",&x[i],&y[i],&c[i]);
T.add(x[i],y[i],(LL)c[i]);
}
t1=T; printf("%lld\n",t1.isap(s,t)); k--;
a.build();
priority_queue<qu> p; p.push(a);
while (!p.empty()) {
cur=cur1=p.top(); p.pop();
if (cur.val<inf) printf("%lld\n",cur.val),k--;
else break;
if (!k||!cur.now) break;
cur.able[cur.now]=1; cur.build();
if (cur.val<inf)
p.push(cur);
cur1.uab[cur1.now]=1; cur1.build();
if (cur1.val<inf)
p.push(cur1);
}
if (k) printf("-1\n");
}
}