【BZOJ 1189】[HNOI2007] 紧急疏散evacuate

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

这题还是很好想的,要么二分时间,要么时间上暴力
然而HZWER的做法是错误的,门那里肯定需要按时间拆点

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

const int N = 500000;
const int M = 2000000;
const int L = 30;
const int INF = 10000000;

int n,m,mat[L][L],S,T,vout,dis[N],cur[N];
int nxt[M],to[M],flow[M],head[N],nod_cnt;
char pat[L]; vector<int> num[N],dor; queue<int> que;

#define id(x,y) ((x)+((y)-1)*n)
inline void Add_Edge(int u, int v, int f){
	static int TT = 1;
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f;
	to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0;
}

inline bool BFS(){
	memset(dis,-1,sizeof(dis));
	dis[S] = 0; que.push(S);
	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]));
			ret += tmp; f -= tmp; flow[i] -= tmp; flow[i^1] += 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 search(int s){
	memset(dis,-1,sizeof(dis));
	que.push(s); dis[s] = 0; 
	
	while (!que.empty()) {
		int w = que.front(); que.pop();
		int x=w, y=0; while ((y+1)*n < w) y++; x -= y*n; y++;
		if (mat[x][y] == 1) Add_Edge(w,num[s][dis[w]],1);
		if (x - 1 >= 1 && !~dis[id(x-1,y)] && mat[x-1][y] == 1) que.push(id(x-1,y)), dis[id(x-1,y)] = dis[w] + 1;
		if (x + 1 <= n && !~dis[id(x+1,y)] && mat[x+1][y] == 1) que.push(id(x+1,y)), dis[id(x+1,y)] = dis[w] + 1;
		if (y - 1 >= 1 && !~dis[id(x,y-1)] && mat[x][y-1] == 1) que.push(id(x,y-1)), dis[id(x,y-1)] = dis[w] + 1;
		if (y + 1 <= m && !~dis[id(x,y+1)] && mat[x][y+1] == 1) que.push(id(x,y+1)), dis[id(x,y+1)] = dis[w] + 1;
	}
}

int main(){
	cin>>m>>n; S = 0; T = N-1; nod_cnt = n*m;
	for (int j=1;j<=m;j++) {
		scanf("%s",pat+1);
		for (int i=1;i<=n;i++) 
			if (pat[i] == 'X') mat[i][j] = 2;
			else if (pat[i] == '.') mat[i][j] = 1, vout++, Add_Edge(S,id(i,j),1);
			else dor.push_back(id(i,j));
	} 
	for (int i=0,lim=dor.size();i<lim;i++) for (int k=1;k<=n*m;k++) num[dor[i]].push_back(++nod_cnt);
	for (int i=0,lim=dor.size();i<lim;i++) search(dor[i]);
	for (int k=0,ret=0;k<n*m;k++) {
		ret += Dinic();
		if (ret == vout) printf("%d\n",k), exit(0);
		for (int i=0,lim=dor.size();i<lim;i++) 
			Add_Edge(num[dor[i]][k],num[dor[i]][k+1],INF),
			Add_Edge(num[dor[i]][k+1],T,1);
	} puts("impossible");
	return 0;
}

【COGS 728】[网络流24题] 最小路径覆盖问题

题目传送门:http://cojs.tk/cogs/problem/problem.php?pid=728

这个玩意,我们可以参考:https://oi.men.ci/cogs-728/
也可以参考:https://www.byvoid.com/blog/lpf24-3
这题我觉得可以想成是点的入读与出度之间相互匹配

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

const int N = 10000;
const int M = 100000;
const int INF = 1000000;

int head[N],cur[N],dis[N],nxt[M],to[M],flow[M];
int vout,n,m,S,T,vis[N]; 
queue<int> que;

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 void Add_Edge(int u, int v, int f){
	static int TT = 1;
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f;
	to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0;
}

inline bool BFS(){
	memset(dis,-1,sizeof(dis));
	dis[S] = 0; que.push(S);
	
	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]));
			ret += tmp; f -= tmp; flow[i] -= tmp; flow[i^1] += 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;
}

void print(int w) {
	vis[w] = 1; printf("%d ",w);
	for (int i=head[w];i;i=nxt[i]) if (!flow[i] && !vis[to[i]])
		{print(to[i]>n?to[i]-n:to[i]); break;}
}

int main(){
	freopen("path3.in","r",stdin);
	freopen("path3.out","w",stdout);
	vout = n = read(); m = read(); S = 0; T = N-1; vis[S] = vis[T] = 1;
	for (int i=1,u,v;i<=m;i++) u = read(), v = read(),	Add_Edge(u,v+n,1);
	for (int i=1;i<=n;i++) Add_Edge(S,i,1), Add_Edge(i+n,T,1);
	vout -= Dinic();
	for (int i=1,w;i<=n;i++) if (!vis[i])
		print(i), putchar('\n');
	printf("%d\n",vout);
	return 0;
}

值得一提的是,这题的SPJ居然对于文末回车敏感 (╯‵□′)╯︵┻━┻
害的老子调了一个小时 QAQ

【BZOJ 1877】[SDOI2009] 晨跑

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

喜闻乐见大水题!

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

const int N = 200*2+9;
const int M = 50000+9;
const int INF = 100000000;

int head[N],dis[N],nxt[M],cost[M],to[M],flow[M]; 
int n,m,S,T,F,C,inq[N],cur[N],sur[N];
queue<int> que;

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;
}

#define id(x,ty) ((x)+n*(ty))
inline void Add_Edge(int u, int v, int f, int c) {
	static int TT = 1;
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f; cost[TT] = c;
	to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0; cost[TT] = -c;
}

inline bool SPFA(){
	memset(dis,-1,sizeof(dis));
	dis[S] = 0; que.push(S); inq[S] = 1; cur[S] = INF;
	
	while (!que.empty()) {
		int w = que.front(); que.pop(); inq[w] = 0;
		for (int i=head[w];i;i=nxt[i]) if (flow[i])
			if (!~dis[to[i]] || dis[to[i]] > dis[w] + cost[i]) {
				sur[to[i]] = i;
				dis[to[i]] = dis[w] + cost[i]; 
				cur[to[i]] = min(flow[i], cur[w]);
				if (!inq[to[i]]) que.push(to[i]), inq[to[i]] = 1;
			}
	}
	return ~dis[T];
}

inline void MCMF(){
	for (int w=T;SPFA();w=T) {
		F += cur[T];
		while (w != S) {
			flow[sur[w]] -= cur[T];
			flow[sur[w]^1] += cur[T];
			C += cur[T]*cost[sur[w]];
			w = to[sur[w]^1];
		}
	}
}

int main(){
	n = read(); m = read(); S = id(1,1); T = id(n,0);
	for (int i=2;i<n;i++) Add_Edge(id(i,0),id(i,1),1,0);
	for (int i=1,u,v,c;i<=m;i++) 
		u = read(), v = read(), c = read(),
		Add_Edge(id(u,1),id(v,0),1,c);
	MCMF();
	printf("%d %d\n",F,C);
	return 0;
}

【BZOJ 1066】[SCOI2007] 蜥蜴

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

喜闻乐见大水题!

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

const int L = 25;
const int N = L*L*2+9;
const int M = N*100;
const int INF = 10000000;

int nxt[M],to[M],flow[M],head[N],dis[N],cur[N];
int m,n,d,mat[L][L],vout,S,T;
char pat[N]; queue<int> que;

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;
}

#define id(x,y,ty) ((x)+((y)-1)*n+(ty)*m*n)
inline void Add_Edge(int u, int v, int f){
	static int TT = 1;
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f;
	to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0;
}

inline bool BFS(){
	memset(dis,-1,sizeof(dis));
	dis[S] = 0; que.push(S);
	
	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]));
			ret += tmp; f -= tmp; flow[i] -= tmp; flow[i^1] += 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;
}

int main(){
	m = read(); n = read(); d = read(); S = 0; T = N-1;
	for (int j=1;j<=m;j++) {
		scanf("%s",pat+1);
		for (int i=1;i<=n;i++) mat[i][j] = pat[i] - '0';
		for (int i=1;i<=n;i++) if (mat[i][j]) Add_Edge(id(i,j,0),id(i,j,1),mat[i][j]);
	}
	for (int j=1;j<=m;j++) {
		scanf("%s",pat+1);
		for (int i=1;i<=n;i++) if (pat[i] == 'L') Add_Edge(S,id(i,j,0),1), vout++;
	}
	for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) if (mat[i][j]) {
		if (min(min(i,n-i+1),min(j,m-j+1)) <= d) Add_Edge(id(i,j,1),T,INF);
		for (int x=1;x<=n;x++) for (int y=1;y<=m;y++) 
			if (mat[x][y] && abs(x-i)+abs(y-j) <= d) Add_Edge(id(i,j,1),id(x,y,0),INF);
	}
	vout -= Dinic();
	printf("%d\n",vout);
	return 0; 
}

【COGS 746】[网络流24题] 骑士共存

题目传送门:http://cojs.tk/cogs/problem/problem.php?pid=746

好吧,我承认我看的是题解……
推荐这家的题解:https://oi.men.ci/cogs-746/

自己来说的话,就是搞一搞二分图?
染色之后,发现有冲突的点都在不同的集合内
于是连边,跑最小割即可

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

const int N = 40000+9;
const int M = N*10;
const int L = 200+9;
const int INF = 10000000;

int head[N],nxt[M],to[M],flow[M],cur[N],dis[N];
int n,m,mat[L][L],vout,S,T; 
int dx[]={0,2,1,-1,-2,-2,-1,1,2};
int dy[]={0,1,2,2,1,-1,-2,-2,-1};
queue<int> que;

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;
}

#define id(x,y) ((x)+((y)-1)*n)
inline void Add_Edge(int u, int v, int f){
	static int TT = 1;
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; flow[TT] = f;
	to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; flow[TT] = 0;
}

inline bool BFS(){
	memset(dis,-1,sizeof(dis));
	dis[S] = 0; que.push(S);
	
	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]));
			ret += tmp; f -= tmp; flow[i] -= tmp; flow[i^1] += 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;
}

int main(){
	freopen("knight.in","r",stdin);
	freopen("knight.out","w",stdout);
	n = read(); m = read(); S = 0; T = N-1; vout = n*n;
	for (int i=1,x,y;i<=m;i++) x = read(), y = read(), mat[x][y] = 1, vout--;
	for (int j=1;j<=n;j++) for (int i=1;i<=n;i++) if (!mat[i][j]) 
		if ((i+j)&1) {
			Add_Edge(S,id(i,j),1); 
			for (int k=1,x,y;k<=8;k++) {
				x = i + dx[k]; y = j + dy[k];
				if (1 <= x && x <= n && 1 <= y && y <= n && !mat[x][y])
					Add_Edge(id(i,j),id(x,y),INF);
			}
		} else Add_Edge(id(i,j),T,1);
	vout -= Dinic();
	printf("%d\n",vout);
	return 0;
}

【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;
}

【BZOJ 4070】[APIO2015] 雅加达的摩天楼

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

这个题目,第一眼看就是最短路
然而O(n^2)条边真的是过不了
于是我们分类讨论,将p小于sqrt(n)的边全部建出来,p大于的暴力建
貌似叫分层图?还是看这里吧:http://blog.csdn.net/aarongzk/article/details/51195437?hmsr=toutiao.io
然而随便出一个数据就可以把这个做法搞死QAQ
管他的,卡过就好

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
#define NUM(p,f) (((p)-1)*(gap+1)+(f))
using namespace std;

const int N = 30005*110;
const int M = 30005*550;
const int INF = 0x3f;
const int inf = 0x3f3f3f3f;

struct Edge{int to,nxt,cost;}e[M];
int head[N],dis[N],inq[N],S,T,n,m,gap;
queue<int> que;

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 void AddEdge(int u, int v, int c){
	static int TT = 0; e[++TT].cost = c; 
	e[TT].to = v; e[TT].nxt = head[u]; head[u] = TT;
}

inline int SPFA(int u, int v){
	memset(dis,INF,sizeof(dis));
	dis[u] = 0; que.push(u); inq[u] = 1;
	while (!que.empty()) {
		int w = que.front(); que.pop(); inq[w] = 0;
		for (int i=head[w];i;i=e[i].nxt) if (dis[w]+e[i].cost < dis[e[i].to]) {
			int to = e[i].to;
			dis[to] = dis[w] + e[i].cost;
			if (!inq[to]) que.push(to), inq[to] = 1;
		}
	} 
	if (dis[v] == inf) return -1;
	else return dis[v];
}

int main(){
	n = read(); m = read(); gap = max(1,min(100,(int)sqrt(n))); 
	for (int i=1;i<=n;i++) for (int j=1;j<=gap;j++) AddEdge(NUM(i,j),NUM(i,0),0);
	for (int i=1;i<n;i++) for (int j=1;j<=gap;j++) if (i+j <= n) AddEdge(NUM(i,j),NUM(i+j,j),1); else break;
	for (int i=2;i<=n;i++) for (int j=1;j<=gap;j++) if (i-j > 0) AddEdge(NUM(i,j),NUM(i-j,j),1); else break; 
	for (int i=1,pos,rag;i<=m;i++) {
		pos = read()+1; rag = read();
		if (i == 1) S = NUM(pos,0); if (i == 2) T = NUM(pos,0);
		if (rag > gap) {
			for (int j=pos+rag,t=1;j<=n;j+=rag,t++) AddEdge(NUM(pos,0),NUM(j,0),t);
			for (int j=pos-rag,t=1;j>0;j-=rag,t++) AddEdge(NUM(pos,0),NUM(j,0),t);
		} else AddEdge(NUM(pos,0),NUM(pos,rag),0);
	} printf("%d\n",SPFA(S,T)); 
	return 0;
}

【Codeforces 700C】Break Up

题目传送门:http://codeforces.com/problemset/problem/700/C
官方题解:http://codeforces.com/blog/entry/46283

最开始,瞄一眼:“这™不狼抓兔子吗?”
于是死坑在网络流上
发现,网络流只能保证总费用最小,但实在是保证不了边数最小
比如这个图:34167845631254
于是还是按照官方给的题解,码的求割顶的代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

const int N = 1000+9;
const int M = 60000+9;
const int INF = 0x7fffffff;

int n,m,s,t,vout=INF,vis[N],tag[M],tot,sign,upd;
int head[N],nxt[M],to[M],val[M],link[N],num[N];
 queue<int> que,path,ans;

inline void AddEdge(int a, int b, int w){
	static int T = 1;
	to[++T] = b; nxt[T] = head[a]; head[a] = T; val[T] = w;
	to[++T] = a; nxt[T] = head[b]; head[b] = T; val[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;
}

bool DFS(int w){
	if (w == t) return 1; else vis[w] = 1;
	for (int i=head[w];i;i=nxt[i]) if (!vis[to[i]]) 
		if (DFS(to[i])) {path.push(i); return 1;}
	return 0;
}

int Get_Cut(int w, int f){
	if (!num[w]) link[w] = num[w] = ++tot; int ret = w==t?1:0;
	for (int i=head[w];i;i=nxt[i]) if (!tag[i] && (i^1) != f) {
		if (num[to[i]]) link[w] = min(link[w],num[to[i]]);
		else {
			int tmp = Get_Cut(to[i],i);
			if (link[to[i]] > num[w]) {if (tmp && (upd==INF || val[upd] > val[i])) upd = i;} 
			else link[w] = min(link[w], link[to[i]]);
			ret |= tmp;
		}
	}
	return ret;
}

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

inline void update(int a, int b){
	if (vout > val[a] + val[b]) {
		vout = val[a] + val[b];
		while (!ans.empty()) ans.pop(); 
		ans.push(a); if (b) ans.push(b);
	}
}

int main(){
	n = read(); m = read(); s = read(); t = read();
	for (int i=1,u,v,w;i<=m;i++) u=read(), v=read(), w=read(), AddEdge(u,v,w);
	if (DFS(s)) {
		while (!path.empty()) {
			int w = path.front(); 
			tag[w] = tag[w^1] = 1;
			if (BFS()) {
				memset(link,0,sizeof(link)); 
				memset(num,0,sizeof(num)); upd = INF;
				link[s] = num[s] = tot = 1; Get_Cut(s,-1);
				if (upd != INF) update(upd,w);
			} else update(w,0);
			tag[w] = tag[w^1] = 0; path.pop();
		}
		if (vout == INF) cout<<-1;
		else {
			cout<<vout<<endl<<ans.size()<<endl;
			while (!ans.empty()) cout<<ans.front()/2<<' ', ans.pop();
		}
	} else cout<<0<<endl<<0;	
	return 0;
}

【算法笔记】2-SAT

好久没有见到这种清新脱俗的算法了!
刘汝佳的白书上面有这个东西,所以基础部分就自行翻阅啦!

唯一想说的是,有一种O(M)判定是否存在解的算法:
直接求一个SCC,然后看那两个点是否在同一个SCC里即可
当然这种算法也可以求出一组可行解,
但貌似刘汝佳的算法时间勉强够用了,所以就不管了吧( •̀ ω •́ )y

【BZOJ 1997】[Hnoi2010] Planar

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

这个题目在知道了是2-sat的情况下还是懵逼了好久QAQ
当时就想到这个图:一个正方形加上对角线.jpg(solidwork还没装,不想用word画图QAQ
发现他给的这个圆不能自交,于是每一条线就只有两种可能:
1.圆内
2.圆外
于是处理出相互干涉的线段,然后搞2-sat

最开始这么搞了,血RE
遂看题解,™有一个这个结论:
Theorem 1. If v ≥ 3 then e ≤ 3v − 6;
Theorem 2. If v ≥ 3 and there are no cycles of length 3, then e ≤ 2v − 4.
详细情况:https://en.wikipedia.org/wiki/Planar_graph
于是加特判,遂过

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<set>
#include<map>
using namespace std;

const int MAXN = 20000+9;
const int N = 200000;

int n,m,que[MAXN],L[MAXN],R[MAXN],T,nxt[N],to[N],head[MAXN],mark[MAXN],cnt;
vector<int> G[MAXN];
map<int,int> ins;

struct CMP{inline bool operator () (const int &A, const int &B) {return R[A] < R[B];}};
multiset<int,CMP>::iterator itr;
multiset<int,CMP> buf;


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

inline void init(){
	n = read(); m = read(); T = 0;
	for (int i=1;i<=n;i++) G[i].clear();
	memset(head,0,sizeof(head));
	memset(mark,0,sizeof(mark));
}

#define Add_Edge(a,b) to[++T] = b,nxt[T]=head[a],head[a]=T
inline void AddEdge(int a, int b) {
	int ra=a*2+1, rb=b*2+1; a *= 2; b *= 2;
	Add_Edge(ra,b); Add_Edge(rb,a);
	Add_Edge(a,rb); Add_Edge(b,ra);
}

inline void build_map(){
	buf.clear();
	for (int i=1;i<=n;i++) {
		while (buf.size() && R[*(buf.begin())] <= i) buf.erase(buf.begin());
		for (int j=0,sz=G[i].size();j<sz;j++) 
			for (itr=buf.begin();itr!=buf.end() && R[*itr] < R[G[i][j]];itr++) 
				AddEdge(G[i][j],*itr);
		for (int j=0,sz=G[i].size();j<sz;j++) buf.insert(G[i][j]);
	}
}

bool DFS(int w) {
	if (mark[w]) return true;
	if (mark[w^1]) return false;
	mark[w] = 1; que[++cnt] = w;
	
	for (int i=head[w];i;i=nxt[i]) 
		if (!DFS(to[i])) return false;
	return true;
}

inline bool judge(){
	for (int i=2;i<=m*2+1;i+=2) if (!mark[i] && !mark[i+1]) {
		cnt = 0; if (DFS(i)) continue;
		for (int j=1;j<=cnt;j++) mark[que[j]] = 0;
		cnt = 0; if (!DFS(i+1)) return false; 
	}
	return true;
}

int main(){
	int TT = read(); while (TT--) { init();
		for (int i=1;i<=m;i++) L[i] = read(), R[i] = read();
		for (int i=1;i<=n;i++) que[i] = read(), ins[que[i]] = i;
		for (int i=1;i<=m;i++) {
			L[i] = ins[L[i]]; R[i] = ins[R[i]];
			if (L[i] > R[i]) swap(L[i], R[i]);
			G[L[i]].push_back(i);
		}
		if (m > 3*n-6) printf("NO\n");
		else {
			build_map();
			if (judge()) printf("YES\n");
			else printf("NO\n");
		}
	} 
	return 0;
}

【BZOJ 2199】[Usaco2011 Jan] 奶牛议会

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2199
离线版题目:http://paste.ubuntu.com/22149982/

仍然是2-sat
一开始觉得这货,拓扑关系上的父亲对该决策有影响
遂wa
后来想一想,貌似父亲那过来的条件只是充分的,不是必要的
于是O(n^2)暴力验证,ac

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

const int MAXN = 8000+9;

int T,nxt[MAXN],to[MAXN],head[MAXN],mark[MAXN];
int que[MAXN],cnt,vout[MAXN],n,m;

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

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

inline bool DFS(int w){
	if (mark[w^1]) return false;
	else if (mark[w]) return true;
	mark[w] = 1; que[++cnt] = w;
	
	for (int i=head[w];i;i=nxt[i]) 
		if (!DFS(to[i])) return false;
	return true;
}

inline bool Get_Ans(){
	for (int i=2;i<=n*2+1;i+=2) if (!mark[i] && !mark[i+1]) {
		cnt = 0; if (DFS(i)) vout[i/2] = 1;
		for (int j=1;j<=cnt;j++) mark[que[j]] = 0;
		cnt = 0; if (DFS(i+1)) vout[i/2] += 2;
		for (int j=1;j<=cnt;j++) mark[que[j]] = 0;
		if (!vout[i/2]) return false;
	}
	return true;
}

int main(){
	n = read(); m = read();
	for (int i=1,a,b,ra,rb;i<=m;i++) { char tmp; 
		a = ra = read()*2; scanf("%c",&tmp);
		if (tmp == 'Y') ra++; else a++;
		b = rb = read()*2; scanf("%c",&tmp);
		if (tmp == 'Y') rb++; else b++;
		AddEdge(ra,b); AddEdge(rb,a);
	} 
	if (!Get_Ans()) printf("IMPOSSIBLE");
	else for (int i=1;i<=n;i++) {
		if (vout[i] == 3) printf("?");
		else if (vout[i] == 1) printf("Y");
		else printf("N");
	}
	return 0;
} 

【BZOJ 1823】[JSOI2010] 满汉全席

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

2-sat的裸题
如果一个评委是喜欢h1 && m3
那么为了满足该评委一定有:m1 -> m3 或 h3 -> h1
™读入那里写挫了,wa了一上午QAQ

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

const int MAXN = 2000+9;

int T,nxt[MAXN],to[MAXN],head[MAXN],n,m,mark[MAXN],que[MAXN],cnt;
char pat[10];

inline char Get(){
	char c=getchar();
	while (c != 'm' && c != 'h') c=getchar();
	return c;
}

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

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

inline void init(){
	scanf("%d%d",&n,&m); T = 0;
	memset(head,0,sizeof(head));
	memset(mark,0,sizeof(mark));
}

bool DFS(int w){
	if (mark[w^1]) return false;
	if (mark[w]) return true;
	mark[w] = 1; que[++cnt] = w;
	
	for (int i=head[w];i;i=nxt[i]) 
		if (!DFS(to[i])) return false;
	return true;
}

inline bool judge(){
	for (int i=1*2;i<=n*2+1;i+=2) if (!mark[i] && !mark[i+1]) { 
		cnt = 0; if (DFS(i)) continue;
		else {
			for (int j=1;j<=cnt;j++) mark[que[j]] = 0; 
			cnt = 0; if (!DFS(i+1)) return false;
		}
	}
	return true;
}

int main(){
	int TT; scanf("%d",&TT);
	while (TT--) {
		init();
		for (int i=1,a,b,ra,rb;i<=m;i++) {
			pat[1] = Get(); a = ra = read()*2;
			pat[4] = Get(); b = rb = read()*2;
			if (pat[1] == 'm') a++; else ra++;
			if (pat[4] == 'm') b++; else rb++;
			AddEdge(ra,b);
			AddEdge(rb,a);
		}
		if (judge()) printf("GOOD\n");
		else printf("BAD\n");
	}
	return 0;
} 

【BZOJ 2438】[中山市选2011] 杀人游戏

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2438
数据生成器:http://paste.ubuntu.com/19584806/
PS:此题的随机数据不容易拍出错QAQ

来练习Tarjan的scc,然而此题细节比较多,被卡了好久QAQ

很明显,如果一个scc中知道了一个点,则其他点都可以安全推出
所以我们只需要搞出入度为零的scc有多少个即可

然而还有一个坑:
如果最后问得只剩一个单独的点了,这个点是不需要询问的
所以遇到这种单独的点,并且他连向的点的入度都不为一的话,ans -= 1

#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
using namespace std;

const int MAXN = 300000+9;

int n,m,to[MAXN],nxt[MAXN],head[MAXN];
int num[MAXN],lowlink[MAXN],in[MAXN],vout;
int scc_num[MAXN],scc_sz[MAXN],scc_cnt;
stack<int> sta;

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

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;
}

void DFS(int w) {
	static int tot = 0; sta.push(w);
	num[w] = lowlink[w] = ++tot;
	for (int i=head[w];i;i=nxt[i]) {
		if (!num[to[i]]) DFS(to[i]), lowlink[w] = min(lowlink[w], lowlink[to[i]]);
		else if (!scc_num[to[i]]) lowlink[w] = min(lowlink[w], lowlink[to[i]]);
	}
	if (lowlink[w] == num[w]) {
		scc_cnt += 1;
		while (true) {
			int nw = sta.top(); sta.pop();
			scc_num[nw] = scc_cnt;
			scc_sz[scc_cnt]++;
			if (nw == w) break;
		}
	}
}

int main(){
	n = read(); m = read(); if (n==1) {printf("1.000000\n"); return 0;}
	for (int i=1,u,v;i<=m;i++) u = read(), v = read(), AddEdge(u,v); 
	for (int i=1;i<=n;i++) if (!num[i]) DFS(i);
	for (int i=1;i<=n;i++) for (int j=head[i];j;j=nxt[j])
		if (scc_num[to[j]] != scc_num[i]) in[scc_num[to[j]]]++;
	for (int i=1;i<=scc_cnt;i++) if (!in[i]) vout++; 
	for (int i=1,tag=0;i<=n;i++,tag=0) if (scc_sz[scc_num[i]] == 1 && !in[scc_num[i]]) {
		for (int j=head[i];j;j=nxt[j]) if (in[scc_num[to[j]]] == 1 && scc_num[to[j]] != scc_num[i]) {tag = 1; break;}
		if (!tag) {vout--; break;}
	}
	printf("%.6lf\n",1.0-(double)vout/n);
	return 0;
} 

为了学习仙人掌才来看的这个,然而现在发现仙人掌用的是无向图的TarjanQAQ

【Timus 1099】Work Scheduling

题目传送门:http://acm.timus.ru/problem.aspx?space=1&num=1099
离线版题目:http://paste.ubuntu.com/19164744/

一般图匹配的板题
然而抄范大神的代码抄错了,调了一个下午QAQ
详细情况见之后的博文吧,会单独写一篇算法笔记的

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

const int MAXN = 1000000;

int n,match[MAXN],fa[MAXN],que[MAXN],fro,bak;
int next[MAXN],mark[MAXN],vis[MAXN];
int T,nxt[MAXN],head[MAXN],to[MAXN];

inline int find(int w){
	int f = fa[w], tmp;
	while (f != fa[f]) f = fa[f];
	while (w != f) tmp=fa[w], fa[w]=f, w=tmp;
	return f;
}

inline void merge(int a, int b){
	int f1 = find(a), f2 = find(b);
	if (f1 != f2) fa[f1] = f2;
}

inline int LCA(int x, int y){
	static int t = 0; t++;
	while (true){
		if (x) {
			x = find(x);
			if (vis[x] == t) return x;
			else {
				vis[x] = t;
				x = next[match[x]];
			}
		} swap(x, y);
	}
}

inline void gather(int a, int p){
	while (a != p){
		int b = match[a], c = next[b];
		if (find(c) != p) next = b;
		if (mark[b] == 2) mark[que[++fro]=b] = 1;
		if (mark == 2) mark[que[++fro]=c] = 1;
		merge(a, b); merge(b, c);
		a = c;
	}
}

inline void Augment(int s){
	for (int i=1;i<=n;i++) next[i]=mark[i]=vis[i]=0, fa[i]=i;
	mark[s] = 1; que[fro=bak=1] = s; 
	while (bak <= fro) {
		int w = que[bak++];
		for (int i=head[w];i;i=nxt[i]){
			if (match[w] == to[i]) continue;
			else if (find(to[i]) == find(w)) continue;
			else if (mark[to[i]] == 2) continue;
			else if (mark[to[i]] == 1) {
				int lca = LCA(w, to[i]);
				if (find(w) != lca) next[w] = to[i];
				if (find(to[i]) != lca) next[to[i]] = w;
				gather(w, lca);
				gather(to[i], lca);
			} else if (!match[to[i]]) {
				next[to[i]] = w;
				for (int u=to[i]; u; ) {
					int v = next[u];
					int fv = match[v];
					match[v] = u; match[u] = v;
					u = fv;
				}
				return;
			} else {
				next[to[i]] = w;
				mark[que[++fro] = match[to[i]]] = 1;
				mark[to[i]] = 2; 
			}
		}
	}
}

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

int main(){
	scanf("%d",&n); int a,b;
	while (~scanf("%d%d",&a,&b)) AddEdge(a, b);
	for (int i=1;i<=n;i++) if (!match[i]) Augment(i);
	
	int vout = 0;
	for (int i=1;i<=n;i++) if (match[i]) vout++;
	printf("%d\n",vout);
	for (int i=1;i<=n;i++) if (match[i] > i) printf("%d %d\n",i,match[i]);
	return 0;
}

另外,此题有坑QAQ
边数巨多,TM还不告诉你是RE,给你说的是TLEQAQ

【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;
}