【BZOJ 4625】[BeiJing2016] 水晶

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4625
数据生成器:http://paste.ubuntu.com/23154162/

这个题目网上为什么搜不到题解QAQ
那我就来撸一份题解吧 = ̄ω ̄=

我们将六边形按照 (x+y+z)%3 == 0/1/2 来分类(染色)
不难发现,会变成这个样子:
486456445
之后,我们发现:两种共振一定是三个不同颜色的点产生的
于是把两种共振合起来考虑
因为我们必须任意三个中至少删一个,于是考虑最小割模型
将%3=1的点和S连一起,%3=2的和T连一起,%3=0的拆成两个点
现在产生共振的话,就连在一起。
这样的话,刚好符合题目的要求,于是跑一跑Dinic即可

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 500000+9;
const int M = 1000000+9;
const int SGZ = 4100+9;
const int INF = 100000000;

int n,mat[SGZ][SGZ],idx[SGZ][SGZ],S,T,vout;
int nxt[M],head[N],to[M],flow[M],dis[N],cur[N];
struct Point{int x,y,t,c;}p[N];
queue<int> que;

inline bool cmp(const Point &A, const Point &B) {return A.x == B.x && A.y == B.y;}
inline bool CMP(const Point &A, const Point &B) {return A.x < B.x || (A.x == B.x && A.y < B.y);}

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline bool BFS(){
	memset(dis,-1,sizeof(dis));
	que.push(S); dis[S] = 0;
	
	while (!que.empty()) {
		int w = que.front(); que.pop(); 
		for (int i=head[w];i;i=nxt[i]) if (flow[i] && !~dis[to[i]]) 
			dis[to[i]] = dis[w] + 1, que.push(to[i]);
	}
	return ~dis[T];
}

int DFS(int w, int f) {
	if (w == T) return f;
	else {
		int ret = 0;
		for (int &i=cur[w];i;i=nxt[i]) if (flow[i] && dis[to[i]] == dis[w] + 1) {
			int tmp = DFS(to[i], min(f,flow[i]));
			flow[i] -= tmp; flow[i^1] += tmp;
			f -= tmp; ret += tmp;
			if (!f)	break;		
		} 
		return ret;
	}
}	

inline int Dinic(){
	int ret = 0;
	while (BFS()) {
		memcpy(cur,head,sizeof(head));
		ret += DFS(S,INF);
	} return ret;
}

inline void Add_Edge(int u, int v, int f){
	static int T = 1;
	to[++T] = v; nxt[T] = head[u]; head[u] = T; flow[T] = f;
	to[++T] = u; nxt[T] = head[v]; head[v] = T; flow[T] = 0;
}

int main(){
	n = read();
	for (int i=1,x,y,z,c;i<=n;i++) {
		x = read(); y = read(); z = read(); c = read(); 
		x += 2001 - z; y += 2001 - z; p[i].t = (x+y) % 3;  
		c *= ((x+y)%3)?10:11;	mat[x][y] += c; vout += c;
		p[i].x = x; p[i].y = y;
	} 
	sort(p+1,p+1+n,CMP); 
	n = unique(p+1,p+1+n,cmp) - p - 1; 
	S = n*2 + 1, T = n*2 + 2;
	for (int i=1;i<=n;i++) idx[p[i].x][p[i].y] = i, p[i].c = mat[p[i].x][p[i].y];
	for (int i=1;i<=n;i++) 
		if (p[i].t == 1) Add_Edge(S,i,p[i].c);
		else if (p[i].t == 2) Add_Edge(i,T,p[i].c);
		else Add_Edge(i,i+n,p[i].c);
	for (int i=1,x,y;i<=n;i++) if (!p[i].t) {
		x = p[i].x; y = p[i].y;
		if (idx[x+1][y]) Add_Edge(idx[x+1][y],i,INF);
		if (idx[x][y+1]) Add_Edge(idx[x][y+1],i,INF);
		if (idx[x-1][y-1]) Add_Edge(idx[x-1][y-1],i,INF);
		if (idx[x+1][y+1]) Add_Edge(i+n,idx[x+1][y+1],INF);
		if (idx[x][y-1]) Add_Edge(i+n,idx[x][y-1],INF);
		if (idx[x-1][y]) Add_Edge(i+n,idx[x-1][y],INF); 
	}
	vout -= Dinic();
	cout<<vout/10<<'.'<<vout%10;
	return 0;
}

至于怎样如何很自然地想到这样构图
我也不知道
R}AML}}{T7C5Y2FLTM`R%54
已经问了YYY,但他还没有回我QAQ

—– UPD 2016.9.10 —–
YYY告诉我,这货不是很常见的构图?
来源于多米诺骨牌模型?

—– UPD 2016.9.13 —–
刚好看到YYY提到的那一类题目了
http://acm.hit.edu.cn/hoj/problem/view?id=2713

【BZOJ 4519】[Cqoi2016] 不同的最小割

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4519

分治+最小割
具体来说,假设随便选两个点来做一次最小割x
那么左右点被分成了两个点集,而任意一对存在于不同点集的点的最小割与x的割相等
于是只用考虑每个点集内部的点,接下来的事就是坚持信仰了

#include<bits/stdc++.h>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int N = 850+9;
const int M = 8500*2+9;
const int INF = 100000000;

int head[N],nxt[M],to[M],flow[M],cur[N];
int n,m,vout[N*N],cnt,id[N],dis[N],pur,T=1;
queue<int> que;

inline void Add_Edge(int u, int v, int w) {
	to[++T] = v; nxt[T] = head[u]; head[u] = T; flow[T] = w;
	to[++T] = u; nxt[T] = head[v]; head[v] = T; flow[T] = w;
}

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
	return ret*f;
}

inline bool BFS(int s, int t) {
	memset(dis,-1,sizeof(dis));
	que.push(s); dis[s] = 0;
	
	while (!que.empty()) {
		int w = que.front(); que.pop();
		for (int i=head[w];i;i=nxt[i]) if (flow[i] && !~dis[to[i]]) 
			dis[to[i]] = dis[w] + 1, que.push(to[i]);
	}
	return ~dis[t];
}

int DFS(int w, int f) {
	if (w == pur) return f;
	else {
		int ret = 0;
		for (int &i=cur[w];i;i=nxt[i]) if (flow[i] && dis[to[i]] == dis[w] + 1) {
			int tmp = DFS(to[i], min(f, flow[i]));
			flow[i] -= tmp; flow[i^1] += tmp;
			ret += tmp; f -= tmp; if (!f) return ret;
		}
		return ret;
	}	
}

inline int Dinic(int s, int t){
	int ret = 0; pur = t;
	while (BFS(s,t)) {
		memcpy(cur,head,sizeof(head));
		ret += DFS(s,INF);
	} return ret;
}

void SIGN(int w) {
	dis[w] = 1;
	for (int i=head[w];i;i=nxt[i]) 
		if (!~dis[to[i]] && flow[i]) SIGN(to[i]);
}

void solve(int l , int r){
	if (l >= r) return ;
	static int tmp[N]; int L=l-1, R=r+1;
	
	for (int i=2;i<=T;i+=2) flow[i] = flow[i^1] = flow[i] + flow[i^1] >> 1;
	vout[++cnt] = Dinic(id[l],id[r]);
	
	memset(dis,-1,sizeof(dis)); SIGN(id[l]); 
	for (int i=l;i<=r;i++) 
		if (~dis[id[i]]) tmp[++L] = id[i];
		else tmp[--R] = id[i];
	for (int i=l;i<=r;i++) id[i] = tmp[i];
	solve(l,L); solve(R,r);
}

int main(){
	n = read(); m = read();
	for (int i=1,u,v,w;i<=m;i++) u = read(), v = read(), w = read(), Add_Edge(u,v,w);
	for (int i=1;i<=n;i++) id[i] = i; solve(1,n); 
	int tot = 0; sort(vout+1,vout+1+cnt); 
	for (int i=1;i<=cnt;i++) if (vout[i] != vout[i-1]) tot++;
	printf("%d\n",tot);
	return 0;
}

【UOJ 80】二分图最大权匹配

题目传送门:http://uoj.ac/problem/80
离线版题目:http://paste.ubuntu.com/19138267/

本来想拿MCMF来水一发,结果T到死,不想回忆KMQAQ
管他的,先放在这里不管了,今天的主要任务是带花树!

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;

const int MAXN = 600000;
const int INF = 100000000;

int n1,n2,m,s,t; LL vout;
int T=1,to[MAXN],nxt[MAXN],head[MAXN],cap[MAXN],flow[MAXN],cost[MAXN];
int dis[MAXN],que[MAXN],fro,bak,inq[MAXN],from[MAXN],scr[MAXN];

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

inline void AddEdge(int a, int b, int CAP, int COST){
	to[++T] = b; from[T] = a; nxt[T] = head[a]; head[a] = T; cap[T] = CAP; cost[T] = COST;
	to[++T] = a; from[T] = b; nxt[T] = head[b]; head[b] = T; cap[T] = 0; cost[T] = -COST;
}

inline bool SPFA(){
	memset(dis,60,sizeof(dis));
	memset(inq,0,sizeof(inq));
	dis[s] = 0; que[fro=bak=1]=s;
	
	while (bak <= fro){
		int w = que[bak++]; inq[w] = 0;
		for (int i=head[w];i;i=nxt[i]){
			if (flow[i] < cap[i] && dis[to[i]] > dis[w]+cost[i]){
				dis[to[i]] = dis[w] + cost[i]; scr[to[i]] = i;
				if (!inq[to[i]]) inq[to[i]] = 1, que[++fro] = to[i];
			}
		} 
	}
	
	return dis[t] <= 0;
}

inline void MCMF(){
	while (SPFA()){
		int w = t; while (w != s) 
			flow[scr[w]] += 1, flow[scr[w]^1] -= 1, 
			vout += cost[scr[w]], w = from[scr[w]]; 
	}
}

int main(){
	n1 = read(); n2 = read(); m = read();
	s = 0; t = n1+n2+1;
	for (int i=1,a,b,c;i<=m;i++)
		a = read(), b = read(), c = read(),
		AddEdge(a, n1+b, 1, -c);
	for (int i=1;i<=n1;i++) AddEdge(s,i,1,0);
	for (int i=1+n1;i<=n1+n2;i++) AddEdge(i,t,1,0);
	MCMF();
	printf("%lld\n",-vout);
	for (int i=1;i<=n1;i++){
		vout = 0;
		for (int j=head[i];j;j=nxt[j])
			if (flow[j] && to[j] > n1)
				{vout = to[j] - n1; break;}
		printf("%d ",vout);
	}
	return 0;
} 

【UOJ 78】二分图最大匹配

题目传送门:http://uoj.ac/problem/78
离线版题目:http://paste.ubuntu.com/19130171/

今天来搞一搞图论,先来一发二分图。
Dinic此题的时间还是不错的,能踩Dinic的,都只有二分图专门的算法

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN = 5000000+9;
const int INF = 10000000;

int n1,n2,m,s,t,vout;
int T=1,to[MAXN],nxt[MAXN],head[MAXN];
int dis[MAXN],que[MAXN],fro,bak;
int cap[MAXN],flow[MAXN],cur[MAXN];

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

inline void AddEdge(int a, int b, int CAP){
	to[++T] = b; nxt[T] = head[a]; head[a] = T; cap[T] = CAP;
	to[++T] = a; nxt[T] = head[b]; head[b] = T; cap[T] = 0;
}

inline bool BFS(){
	memset(dis,-1,sizeof(dis));
	que[fro=bak=1] = s; dis[s] = 0;
	
	while (bak <= fro) {
		int w = que[bak++];
		for (int i=head[w];i;i=nxt[i])
			if (flow[i] < cap[i] && dis[to[i]] == -1)
				dis[to[i]] = dis[w] + 1, que[++fro] = to[i];
	}
	
	return dis[t] != -1;
}

int MaxFlow(int w, int f){
	if (w == t) return f;
	else {
		int ret = 0;
		for (int &i=cur[w];i;i=nxt[i]){
			if (flow[i] < cap[i] && dis[to[i]] == dis[w] + 1){
				int tmp = MaxFlow(to[i], min(f, cap[i]-flow[i]));
				flow[i] += tmp; flow[i^1] -= tmp;
				ret += tmp; if (ret == f) return ret; 
			}
		}
		return ret;
	}
}

inline void Dinic(){
	while (BFS()){
		for (int i=0;i<=t;i++) cur[i] = head[i];
		vout += MaxFlow(s, INF);
	}
}

int main(){
	n1=read(); n2=read(); m=read();
	s = 0; t = n1 + n2 + 1;
	for (int i=1,a,b;i<=m;i++)
		a = read(), b = read(),
		AddEdge(a,n1+b,1);
	for (int i=1;i<=n1;i++) AddEdge(s,i,1);
	for (int i=n1+1;i<=n1+n2;i++) AddEdge(i,t,1);
	Dinic();
	printf("%d\n",vout);
	for (int w=1;w<=n1;w++){
		vout = 0;
		for (int i=head[w];i;i=nxt[i])
			if (flow[i] && to[i] > n1) {
				vout = to[i]-n1; break;
			}
		printf("%d\n",vout);
	}
	return 0;
} 

【COGS 396】[网络流24题] 魔术球问题

题目传送门:http://cogs.top/cogs/problem/problem.php?pid=396
离线版题目:http://paste.ubuntu.com/18780019/

贪心即可。因为枚举一下,1600以内,加上比自己小的数为完全平方数没有多解,所以只要能加,加上去就好
当然, 正解是最少路径覆盖+二分,然而为什么要闲得蛋疼写Dinic ╮(╯-╰)╭
另外,COGS的浮点运算取整是辣鸡QAQ,害的我wa了好几发……

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

const int MAXN = 10000;
const int MX = 1600;

int n,tot,arr[MAXN],vout;

inline bool judge(int num){
	for (int i=1;i<=tot;i++){
		int tmp = sqrt(arr[i]+num);
		if (tmp*tmp == arr[i]+num){
			arr[i] = num; 
			return true;
		}
	}
	if (tot < n) {arr[++tot] = num; return true;}
	return false;
}

int main(){  
	freopen("balla.in","r",stdin);
	freopen("balla.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=MX;i++)
		if (judge(i)) vout=i;
		else {printf("%d\n",i-1);break;}
	return 0;
} 

【COGS 14】[网络流24题] 搭配飞行员

题目传送门:http://cogs.top/cogs/problem/problem.php?pid=14
离线版题目:http://paste.ubuntu.com/18772859/

二分图匹配板题,Dinic跑一跑就ok啦!

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

inline int read(){
	char c=getchar(); int buf=0,f=1;
	while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0'){buf=buf*10+c-'0';c=getchar();}
	return buf*f;
}

const int MAXN = 10000+9;
const int INF = 1000000000;

int n,m,a,b,T,head[MAXN],to[MAXN],nxt[MAXN],cap[MAXN],vout;
int s,t,cur[MAXN],que[MAXN],fro,bak,dis[MAXN],flow[MAXN];

inline void AddEdge(int a, int b){
	to[++T] = b; nxt[T] = head[a]; head[a] = T; cap[T] = 1;
	to[++T] = a; nxt[T] = head[b]; head[b] = T;  
}

inline bool BFS(){
	memset(dis,-1,sizeof(dis));
	dis[s] = 0; que[fro=bak=1]=s;
	
	while (bak <= fro){
		int w = que[bak++];
		for (int i=head[w];i;i=nxt[i])
			if (flow[i] < cap[i] && dis[to[i]]==-1)
				dis[to[i]] = dis[w] + 1,
				que[++fro] = to[i];
	}
	
	if (dis[t] != -1) return true;
	else return false;
}

int MaxFlow(int w, int f){
	if (w == t) return f;
	else {
		int ret = 0;
		for (int &i=cur[w];i;i=nxt[i]){
			if (flow[i] < cap[i] && dis[to[i]] == dis[w]+1){
				int tmp = MaxFlow(to[i], min(f, cap[i]-flow[i]));
				flow[i] += tmp; flow[i^1] -= tmp;
				ret += tmp; f-=tmp;
				if (!f) return ret;
			}
		}
		return ret;
	}
}

inline void Dinic(){
	while (BFS()){
		for (int i=0;i<=n*2+1;i++)
			cur[i] = head[i];
		vout += MaxFlow(s,INF);
	}
}

int main(){
	freopen("flyer.in","r",stdin);
	freopen("flyer.out","w",stdout);
	n = read(); m = read();
	s = 0; t = 2*n+1; T = 1;
	while (~scanf("%d%d",&a,&b)) AddEdge(a*2,b*2-1);
	for (int i=1;i<=m;i++) AddEdge(s,i*2-1);
	for (int i=m+1;i<=n;i++) AddEdge(i*2,t);
	for (int i=1;i<=n;i++) AddEdge(i*2-1,i*2);
	Dinic();
	printf("%d\n",vout);
	return 0;
}

【BZOJ 1001】[BeiJing2006] 狼抓兔子

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1001
数据生成器:http://paste.ubuntu.com/18772741/

最大流板题,输入搞反了,调了3hQAQ

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
 
const int N = 1000000+9;
const int INF = 1000000000;
 
inline int read(){
    char c=getchar(); int buf=0,f=1;
    while (c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while (c<='9'&c>='0'){buf=buf*10+c-'0';c=getchar();}
    return buf*f;
}
 
int n,m,head[N],to[N*6],nxt[N*6],cap[N*6],T,flow[N*6];
int vout,cur[N],que[N],dis[N],fro,bak,tot,s,t;
 
#define id(x,y) (((y)-1)*n+(x))
 
inline void AddEdge(int u, int v, int MX){
    to[++T] = v; nxt[T] = head[u]; head[u] = T; cap[T] = MX;
    to[++T] = u; nxt[T] = head[v]; head[v] = T; cap[T] = MX;
}
 
inline bool BFS(){
    memset(dis,-1,sizeof(dis));
    que[fro=bak=1] = s; dis[s] = 0;
     
    while (bak <= fro) {
        int w = que[bak++];
        for (int i=head[w];i;i=nxt[i]){
            if (flow[i] < cap[i] && dis[to[i]] == -1)
                dis[to[i]] = dis[w]+1,
                que[++fro] = to[i];
        }
    }
    if (dis[t] == -1) return false;
    else return true;
}
 
int MaxFlow(int w, int f){
    if (w==t) {vout += f;return f;}
    else {
        int ret = 0;
        for (int &i=cur[w],tmp;i;i=nxt[i]){
            if (flow[i] < cap[i] && dis[to[i]]==dis[w]+1){
                tmp = MaxFlow(to[i], min(f, cap[i]-flow[i]));
                f -= tmp; ret += tmp;
                flow[i] += tmp; flow[i^1] -= tmp;
                if (!f) return ret;
            }
        }
        return ret;
    }
}
 
inline void Dinic(){
    tot = id(n,m);
    while (BFS()){
        for (int i=1;i<=tot;i++)
            cur[i] = head[i];
        MaxFlow(s,INF);
    }   
}
 
int main(){
    m = read(); n = read(); s = id(1,1); t = id(n,m); T = 1;
    for (int j=1;j<=m;j++) for (int i=1;i<n;i++) AddEdge(id(i,j),id(i+1,j), read());
    for (int j=1;j<m;j++) for (int i=1;i<=n;i++) AddEdge(id(i,j),id(i,j+1), read());
    for (int j=1;j<m;j++) for (int i=1;i<n;i++) AddEdge(id(i,j),id(i+1,j+1),read());
    if (s != t) Dinic();
    printf("%d\n",vout);
    return 0;
}

【算法笔记】再看网络流

最近几天,重新来看网络流的相关题目,发现之前看的基本上都忘了 QAQ
看了一个上午,有以下收获
1.最大流的正确性依赖于每一条s-t流对应了一种实际方案
2.网络流也可以作为二分的可行解判定方式
3.网络流只能在0/1之中做选择,对于1/2、2/3之类的,可以考虑降到0/1之后算贡献
4.对于割边,可以在残余网络中DFS搞一搞,一端可到源点,一端可到汇点即为割边

另外,提供一点网络流24题的相关资源:
1.完整题解:http://www.snowyjone.me/2015/07/lp-and-flow-a/
2.精选题解:https://blog.sengxian.com/solutions/networkflow-24-all
3.大神题解:https://www.byvoid.com/blog/lpf24-solution
4.OJ传送门:http://cojs.tk/cogs/page/page.php?aid=3