本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000 
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

Description

Input

Output

Sample Input

3 3 1 3 100
1 2 3
2 3 4
1 3 5

Sample Output

8
9
12
-1

正解:搜索+堆+网络流(最小割)

解题报告:

  这道题写了我好久,怒写7KB...

  强行把三个程序组合才A掉...

  有用的博客:http://africamonkey.blog.uoj.ac/blog/108

        http://www.cnblogs.com/New-Godess/p/4348890.html

  Task1:

    注意到前两个测试点n、m很小,直接搜索;

  Task2:

    7到14这8个数据具有共同特点:s有边连向所有非t节点,所有非 s结点有边连向t;

    那么我们考虑对于除了S、T之外的所有点来说,都连向S、T,那么只要任意断掉一条即可成为割,而两条都断掉那自然也是合法的。

    我们可以把题目转化为每个集合有三个元素:a,b,a+b(不妨设a<=b),需要从中选取一个,一共有n-2个集合,问全局选取的总和最小的前k个。

    显然,每个集合都取a时,对于全局而言就是最小割了,下面我们考虑如何通过这个最优值转化出较优值,显然接下来我们需要使得每个集合的选择发生改变,

    不妨作差,把每个集合重新定义为大小为2,元素为{b-a,b}的集合,将其按先后两 个关键字排序,则每次只需加上我当前的值即可。

    这样我们不难得到一个算法:每次把当前的集合做三种变换:

    1、把当前的升级

    2、把当前的降级,后一个升级

    3、直接往后取,把下一个集合的升级。用堆维护即可;

  Task3:

    对于n、m不很大的情况,我们考虑从割的角度解决这个问题,假设我们得到了最小割,想得到次小割,显然我们是把最小割中的某一条边换成另一条边,或者加入一条权值最小的新的边。

    如果直接暴力执行上述操作的话会T,仔细思考就会发现问题关键在于如何优化换边的步骤,其实我们无需每次枚举换哪条边,我们可以考虑最小割中的每条边的两个端点到S、T的割的值,取个             min之后就能得到把这条边换掉之后的增长的代价,从而我们得到了一个简单的思路,只需对于每条边求一遍两个端点到S、T的最小割再取min即可,做法大致就是这样。

    但是有很多很多的细节,难以赘述,看代码吧,有详细注释。

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int MAXN = 150011;
const int MAXM = 300011;
int n,m,S,T,k;
int AA[MAXM],BB[MAXM],CC[MAXM];
int ecnt,first[MAXN],w[MAXM],to[MAXM],next[MAXM];//have direction!!!
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
} //Task1:search
namespace Search{
int cnt,father[MAXN],Tim,vis[MAXN];
bool stop[MAXM];
LL ans[1200011];
inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
inline bool check(int x){
if(x==T) return true; vis[x]=Tim;
for(int i=first[x];i;i=next[i]) {
if(stop[i]) continue ; int v=to[i];
if(vis[v]==Tim) continue;
if(check(v)) return true;
}
return false;
}
inline void dfs(int x,LL val){
if(x==m+1) { Tim++; if(!check(S)) ans[++cnt]=val; return ;}
stop[x]=1; dfs(x+1,val+w[x]);
stop[x]=0; dfs(x+1,val);
}
inline void work(){
int x,y,z;
for(int i=1;i<=m;i++) { x=AA[i]; y=BB[i]; z=CC[i]; next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z; }
dfs(1,0); sort(ans+1,ans+cnt+1);
if(cnt>=k) for(int i=1;i<=k;i++) printf("%lld\n",ans[i]);
else { for(int i=1;i<=cnt;i++) printf("%lld\n",ans[i]); printf("-1"); }
}
} //Task3:S links nodes,nodes link T
//nlogn+klogn
namespace StoT{
LL ans;
struct node{ LL val; int pos,id; inline bool operator < (const node &a)const { return a.val<val; }}tmp,Top;
struct seq{ int x,y; }a[MAXN],b[MAXN];
inline bool cmp(seq q,seq qq){ if(q.x==qq.x) return q.y<qq.y; return q.x<qq.x; }
priority_queue<node>Q;
inline void work(){
int x,y; int cnt=0;
for(int i=1;i<=m;i++) {
x=AA[i]; y=BB[i];
if(x==S) b[y].x=CC[i];
else b[x].y=CC[i];
}
for(int i=1;i<=n;i++) {
if(i==S||i==T) continue;
a[++cnt]=b[i];/*!!!*/
if(a[cnt].x>a[cnt].y) swap(a[cnt].x,a[cnt].y);
x=a[cnt].x; ans+=a[cnt].x; a[cnt].x=a[cnt].y-a[cnt].x; a[cnt].y=x;
}
printf("%lld\n",ans); k--;
sort(a+1,a+cnt+1,cmp);
tmp.val=ans+a[1].x; tmp.pos=1; tmp.id=1; Q.push(tmp);
while(k>0&&(!Q.empty())) {
Top=Q.top(); Q.pop();
printf("%lld\n",Top.val);
//choice 1:把当前的升级
if(Top.id<2) {
tmp.val=Top.val+a[Top.pos].y;
tmp.pos=Top.pos;
tmp.id=2;
Q.push(tmp);
}
//choice 2:把当前的降级,后一个升级
if(Top.id==1 && Top.pos<cnt/*!!!*/) {//只需考虑当前取了第一个元素的情况,当前取了第二个元素的话降级再把后一个升级,等价于直接往后取
tmp.val=Top.val-a[Top.pos].x;
tmp.val+=a[Top.pos+1].x;
tmp.pos=Top.pos+1;
tmp.id=1;
Q.push(tmp);
}
//choice 3:直接往后取,把下一个集合的升级
if(Top.pos<cnt/*!!!*/) {
tmp.val=Top.val+a[Top.pos+1].x;/*!!!*/
tmp.pos=Top.pos+1;
tmp.id=1;
Q.push(tmp);
}
k--;
}
if(k>0) printf("-1");
}
} //Task2:n、m很小,无别的特殊条件
//考虑和Task3的类似做法,我们如何将最小割中的边转化为次小割。
//显然我们考虑最小割中权值最小的边,有两种可能:1、强制不选这条边,即将其删除,再跑一次最小割;2、强制选这条边,再选一条权值最小的边
namespace network_flow{
const int N=52,M=3005;
const int inf = (1<<28);
int X[M],Y[M],W[M],ecnt=1,first[N],next[M],to[M],dui[M*10],head,tail,deep[N];
int ds[N],dt[N];//预处理出每个点到S和T的最小割
bool havs[N],havt[N];//是否已经做过最小割
bool in[M/*!!!*/];//是否在最小割边集中
bool vis[N];
struct Graph{
int w[M];
inline void link(int x,int y,int z){
next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z;
next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=0;
}
inline bool bfs(int s,int t){
int head=tail=0; dui[++tail]=s; int u;
for(int i=1;i<=n;i++) deep[i]=-1; deep[s]=1;
while(head<tail) {
head++; u=dui[head];
for(int i=first[u];i;i=next[i]) {
if(w[i]==0) continue; int v=to[i];
if(deep[v]==-1) { deep[v]=deep[u]+1; dui[++tail]=v; }
}
}
if(deep[t]==-1) return false;
return true;
}
inline LL dinic(int x,int remain,int t){
if(x==t||remain==0) return remain; LL flow=0,f;
for(int i=first[x];i;i=next[i]) {
if(w[i]==0) continue; int v=to[i]; if(deep[v]!=deep[x]+1) continue;
f=dinic(v,min(remain,w[i]),t);
if(f==0) deep[v]=-1; else { flow+=f,remain-=f; w[i]-=f; w[i^1]+=f; if(remain==0) return flow; }
}
return flow;
}
inline LL dinic(int s,int t){
LL tot=0; while(bfs(s,t)) { tot+=dinic(s,inf,t); if(tot>inf) return tot; }
return tot;
}
inline void DFS(int x){
vis[x]=1;
for(int i=first[x];i;i=next[i]) if(w[i]&&(!vis[to[i]])) DFS(to[i]);
}
inline void get_cut(){
memset(vis,0,sizeof(vis)); memset(in,0,sizeof(in));
DFS(S);
for(int i=1;i<=m;i++) if(vis[X[i]] && (!vis[Y[i]])) in[i]=1;//!!!割边的判断
}
}G,lin,lin2;
struct heap_node{
LL val; int id,ww;//选出的边的编号
bool must[M],stop[M];
inline bool operator < (const heap_node &a) const{ return a.val<val; }
inline void build(){//找出最小割中最小的那条边,要么强制其不选再跑一遍最小割;或者强制选再选一条最短的边
val=0; lin=G;//!!!还原
for(int i=1;i<=m;i++) if(must[i]) val+=W[i],lin.w[i<<1]=0,lin.w[i<<1|1]=0; else if(stop[i]) lin.w[i<<1]=inf,lin.w[i<<1|1]=0;
val+=lin.dinic(S,T);
lin.get_cut();
memset(havs,0,sizeof(havs)); memset(havt,0,sizeof(havt));
ww=inf; id=0;
for(int i=1;i<=m;i++) {
if(must[i]||stop[i]) continue;
if(in[i]) {//此处为快速找到次小割的方法,无需真的去再做一遍dinic,只需看一下到S、T的最小割,取min之后即变化值
if(!havs[X[i]]) havs[X[i]]=1,lin2=lin,ds[X[i]/**/]=lin2.dinic(S,X[i]);//为了不破坏原来的残量网络,新建一个再跑
if(!havt[Y[i]]) havt[Y[i]]=1,lin2=lin,dt[Y[i]/**/]=lin2.dinic(Y[i],T);
if(ds[X[i]]/*!!!是点而不是边的最小割*/<ww) ww=ds[X[i]],id=i;
if(dt[Y[i]]/*!!!*/<ww) ww=dt[Y[i]],id=i;
/*if(ww==0) {
id++;
id--;
}*/
}
else if(W[i]<ww) ww=W[i],id=i;
}
val+=ww;
}
}a,b;
priority_queue<heap_node>Q;
inline void work(){
for(int i=1;i<=m;i++) {
X[i]=AA[i]; Y[i]=BB[i]; W[i]=CC[i];
G.link(X[i],Y[i],W[i]);
}
k--; lin=G; printf("%lld\n",lin.dinic(S,T));
a.build(); if(a.val<inf) Q.push(a);
while(k>0 && (!Q.empty())) {
a=b=Q.top(); Q.pop(); printf("%lld\n",a.val);
//强制选
a.must[a.id]=1; a.build(); if(a.val<inf) Q.push(a);
//强制不选
b.stop[b.id]=1; b.build(); if(b.val<inf) Q.push(b);
k--;
}
if(k>0) printf("-1");
}
} inline void work(){
n=getint(); m=getint(); S=getint(); T=getint(); k=getint(); int mx=0;
for(int i=1;i<=m;i++) AA[i]=getint(),BB[i]=getint(),CC[i]=getint(),mx=max(mx,CC[i]);
if(n<=10 && m<=20) Search::work();
else if(n<=50 && m<=1500 && k<=100 && mx<=65536) network_flow::work();
else StoT::work();
} int main()
{
work();
return 0;
}

  

UOJ71 【WC2015】k小割的更多相关文章

  1. bzoj3882 [Wc2015]K小割

    Description Input Output Sample Input 3 3 1 3 100 1 2 3 2 3 4 1 3 5 Sample Output 8 9 12 -1   正解:暴搜+ ...

  2. WC2015 k小割(k短路+暴力+搜索)

    首先这道题不是非同一般的恶心,三个数据层次对应三个程序= = PROBLEM:http://uoj.ac/problems解法: 1~2直接暴力枚举边的选择与否+判断就行了 7~14可以发现是一个平面 ...

  3. [LeetCode] Kth Smallest Element in a Sorted Matrix 有序矩阵中第K小的元素

    Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth ...

  4. [LeetCode] Kth Smallest Element in a BST 二叉搜索树中的第K小的元素

    Given a binary search tree, write a function kthSmallest to find the kth smallest element in it. Not ...

  5. POJ2828 Buy Tickets[树状数组第k小值 倒序]

    Buy Tickets Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 19012   Accepted: 9442 Desc ...

  6. UVA11525 Permutation[康托展开 树状数组求第k小值]

    UVA - 11525 Permutation 题意:输出1~n的所有排列,字典序大小第∑k1Si∗(K−i)!个 学了好多知识 1.康托展开 X=a[n]*(n-1)!+a[n-1]*(n-2)!+ ...

  7. *HDU2852 树状数组(求第K小的数)

    KiKi's K-Number Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

  8. 数组中第K小的数字(Google面试题)

    http://ac.jobdu.com/problem.php?pid=1534 题目1534:数组中第K小的数字 时间限制:2 秒 内存限制:128 兆 特殊判题:否 提交:1120 解决:208 ...

  9. 数据结构2 静态区间第K大/第K小

    给定数组$A[1...N]$, 区间$[L,R]$中第$K$大/小的数的指将$A[L...R]$中的数从大到小/从小到大排序后的第$K$个. "静态"指的是不带修改. 这个问题有多 ...

随机推荐

  1. MS coco数据集下载

    2017年12月02日 23:12:11 阅读数:10411 登录ms-co-co数据集官网,一直不能进入,FQ之后开看到下载链接.有了下载链接下载还是很快的,在我这儿晚上下载,速度能达到7M/s,所 ...

  2. Java内存模型FAQ(一) 什么是内存模型

    原文:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html 第一章 译者:方腾飞 在多核系统中,处理器一般有一层或者多层的缓存,这 ...

  3. linux无线网络配置_转

    转自:http://www.cnblogs.com/dartagnan/archive/2010/12/05/2003521.html   一位资生linux 原文:http://www.hpl.hp ...

  4. java面试的那些事

    跳槽面临的第一个难关那就是面试吧.面试的好坏直接关乎着你年薪的多少.如何顺利完成面试的那些难题,今天我们就从java中复习一下.看看经常面试的知识点,为什么面试这些知识点, 如果你是初级的或刚毕业的j ...

  5. Linux nginx部署laravel

    Composer Composer 是 php 的一个依赖管理工具.它允许你申明项目所依赖的代码库,它会在你的项目中为你安装他们.运行 Composer 需要 PHP 5.3.2+ 以上版本.一些敏感 ...

  6. 【BZOJ3333】排队计划 树状数组+线段树

    [BZOJ3333]排队计划 Description Input Output Sample Input 6 2 160 163 164 161 167 160 2 3 Sample Output 6 ...

  7. 大数据学习系列(5)-- 局域网yum仓库搭建

    https://www.cnblogs.com/nulige/p/6081192.html

  8. jqcloud 标签云效果

    官网地址: http://mistic100.github.io/jQCloud/index.htmlgithub 地址: https://github.com/lucaong/jQCloud使用 & ...

  9. Spring mvc 与 strust

    1. 机制:spring mvc的入口是servlet,而struts2是filter 2. 性能:spring会稍微比struts快.spring mvc是基于方法,单例(servlet也是单例): ...

  10. 程序运行之ELF文件结构

    ELF目标文件格式的最前部是ELF文件头.包含了整个文件的基本属性.比如ELF文件版本,目标机器型号,程序入口地址等.然后是ELF的各个段,其中ELF文件中与段有关的重要结构就是段表.段表描述了ELF ...