【Codevs 1817】灾后重建

题目传送门:http://codevs.cn/problem/1817/

这题可以说是Floyd的最终考验吧?
具体来说:做了这题Floyd的原理就完全明白了吧!
详细请参考《算导》P405吧!(难得算导说了一次人话)

#include<iostream>
#include<cstdio>
#include<vector>
#define INF 1000099 
using namespace std;

struct abc{int point,len;};
int n,m;
int t[299]={0};
int f[299][299];
bool yet[299]={0};
vector<abc> path[299];  
bool day[100099]={0};
int pro=0;

void init(){
	for (int i=1;i<=250;i++){
		t[i]=INF;
	}
	for (int i=0;i<=250;i++){
	    for (int j=0;j<=250;j++){
		    f[i][j]=INF;
	    }
	}
    scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++){
	    scanf("%d",&t[i]); 
	    if (t[i]==0) yet[i]=true;
	}	
	abc hc;
	for (int i=1,a,b,leng;i<=m;i++){
		scanf("%d%d%d",&a,&b,&leng);
		a++;b++;
		f[a][b]=leng;f[b][a]=leng;
		hc.point=b;hc.len=leng;
		path[a].push_back(hc);
		hc.point=a;
		path[b].push_back(hc);
	}
}

void update(int num){
	int end,length;
	for (int k=pro+1;k<=end;k++)
	    yet[k]=true;
	for (int k=pro+1;k<=end;k++){
	    for (int i=1;i<=n;i++){
	    	for (int j=1;j<=n;j++){
	    		f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
	    	}
	    }
	}
	pro=end; 
} 

int main(){
	int Q,x,y,ti;
	init();
	cin>>Q;
	for (int i=1;i<=Q;i++){
		scanf("%d%d%d",&x,&y,&ti);
		x++;y++;
		if (!day[ti]) {
		    update(ti);
			day[ti]=1;
		}
		if (f[x][y]<INF && yet[x] && yet[y]) printf("%d\n",f[x][y]);
		else printf("-1\n");
	}
	return 0;
}

为什么去年写的代码这么丑? (╯‵□′)╯︵┻━┻

【UVa 1027】Toll

题目传送门:https://uva.onlinejudge.org/index.php?problem=3468
中文题面:《算法竞赛·入门经典·训练指南》P331

一直以为是一个建图非常神奇的最短路/网络流
然而居然是类似斯坦纳树一样的转移怪异的DP
qrrxcieey4qta4clrvl

设d[i]为满足题目条件的情况下,到达第i个点时的最少货物量
然后反向各更新即可

更新的方式的话,显然SPFA的正确性没有问题
又因为没有负权,所以Dijkstra也是正确的?

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

const int N = 70;
const int M = 2000+9;
const double EPS = 1e-4;

int head[N],nxt[M],to[M],m,T,MN,s,t,case_cnt;
LL d[N]; bool inq[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 int ID(const char c) {
	if ('a' <= c && c <= 'z') return c - 'a' + 1;
	else return c - 'A' + 27;
}

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

inline LL cost(int w, LL val) {
	if (w == s) return 0;
	else if (w <= 26) return 1;
	else {
		LL l=1,r=1e15,ret=1,mid;
		while (l <= r) {
			mid = l + r >> 1;
			if (val+mid-(LL)(ceil((val+mid)/20.0)+EPS) >= val) ret = mid, r = mid - 1;
			else l = mid+1;
		}
		return ret;
	}
}	

inline LL SPFA() {
	memset(d,0x3f,sizeof(d));
	d[t] = MN + (s!=t?cost(t, MN):0); 
	inq[t] = 1; que.push(t);
	
	while (!que.empty()) {
		int w = que.front(); inq[w] = 0; que.pop();
		for (int i=head[w];i;i=nxt[i]) {
			if (d[to[i]] > d[w] + cost(to[i],d[w])) {
				d[to[i]] = d[w] + cost(to[i],d[w]);
				if (!inq[to[i]]) inq[to[i]] = 1, que.push(to[i]);
			}
		}
	}
	return d[s];
}

int main(){
	static char U[10],V[10];
	for (m=read();~m;m=read(),T=0) {
		memset(head,0,sizeof(head));
		
		for (int i=1;i<=m;i++) {
			scanf("%s %s",U+1,V+1);
			Add_Edge(ID(U[1]), ID(V[1]));
		}
		
		MN = read();
		scanf("%s %s",U+1,V+1);
		s = ID(U[1]); t = ID(V[1]);
		printf("Case %d: %lld\n",++case_cnt,SPFA());
	}
	return 0;
}

【UVa 1416】Warfare And Logistics

题目传送门:https://uva.onlinejudge.org/index.php&problem=4162
中文题面:《算法竞赛·入门经典·训练指南》P330

首先,这题看数据范围就是一个大暴力
然而我只会O(m*n^3)的、会T到死的、辣鸡到爆炸的大暴力
f0754h_js27jw9sby3i

枚举每一个最短路的起点,考虑只有删除最短路树上的边才会影响到答案
于是就只用枚举O(n^2),最终复杂度为O(n^2mlog(n))

【BZOJ 2069】[POI2004] ZAW

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2069
数据传送门:http://oi.cyo.ng/wp-content/uploads/2016/10/2069.rar

这题说两个做法:
1)最短路+奇怪的姿势,时间复杂度O(nlogn)
2)关键点间的最短路,时间复杂度O(nlog^2n)

先说第一种方式:
从1开始跑最短路,每个点记录最短路+次短路,保证路径上第二个点不同(1算第一个点)
然后O(n)扫描每一个点,使用最短路+次短路更新答案即可
这种方法虽然时间复杂度较优,但不是很通用

接下来我们说一说关键点间的最短路:
考虑将答案路径与1相连的两条边删掉
路径变为与1相连的点间的最短路径
于是将与1相邻的点设为关键点,跑关键点间的最短路即可

接下来说一说怎么跑关键点间的最短路:
考虑拆二进制,枚举二进制的每一位。
根据这一位是否为1,将关键点分为两个点集
然后一个连超级源,另一个连超级汇即可
时间复杂度O(nlog^2(n))
其实这才是这篇题解的重点

这里是二进制拆分+Dijkatra的代码,请随意食用~

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

const int N = 50000+9;
const int M = 300000+9;
const int INF = 1e9;

int head[N],dis[N],t1[N],t2[N],t3[N],head_tmp[N],tot,TT,S,T,vout=INF,n,m,nxt[M],to[M],cost[M];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > que; bool vis[N]; 

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 c1, int c2) {
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT; cost[TT] = c1;
	to[++TT] = u; nxt[TT] = head[v]; head[v] = TT; cost[TT] = c2;
}

inline int Dijkstra() {
	memset(dis,60,sizeof(dis));
	memset(vis,0,sizeof(vis));
	que.push(make_pair(0,S));
	dis[S] = 0; vis[1] = 1;
	
	while (!que.empty()) {
		int w = que.top().second; que.pop();
		if (vis[w]) continue;
		else vis[w] = 1; 
		for (int i=head[w];i;i=nxt[i]) {
			if (dis[to[i]] > dis[w] + cost[i] && !vis[to[i]]) {
				dis[to[i]] = dis[w] + cost[i];
				que.push(make_pair(dis[to[i]], to[i]));	
			} 
		}
	}
	return dis[T];
}

int main(){
	n = read(); m = read();
	for (int i=1,a,b,c,d;i<=m;i++) {
		a = read(); b = read();
		c = read(); d = read();
		Add_Edge(a, b, c, d);
		
		if (a == 1) {
			t1[++tot] = b;
			t2[tot] = c;	
			t3[tot] = d;
		} else if (b == 1) {
			t1[++tot] = a;
			t2[tot] = d;
			t3[tot] = c;
		}
	}
	
	S = N - 1; T = N - 2;
	int w = 0, TMP = n, ori = TT;
	while (TMP) w++, TMP >>= 1;
	memcpy(head_tmp,head,sizeof(head));
	
	for (int k=0,cur=1;k<=w;k++,cur<<=1) {
		TT = ori;
		memcpy(head,head_tmp,sizeof(head));
		
		for (int i=1;i<=tot;i++) {
			if (t1[i]&cur) Add_Edge(S,t1[i],t2[i],INF);
			else Add_Edge(t1[i],T,t3[i],INF);
		} 
		vout = min(Dijkstra(), vout);
		
		TT = ori;
		memcpy(head,head_tmp,sizeof(head));
		
		for (int i=1;i<=tot;i++) {
			if (t1[i]&cur) Add_Edge(t1[i],T,t3[i],INF);
			else Add_Edge(S,t1[i],t2[i],INF);
		} 
		vout = min(Dijkstra(), vout);
	}
	printf("%d\n",vout);
	return 0;
}

【BZOJ 3345】[Pku2914] Minimum Cut

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

这题就是一个全局最小割,其中分治最小割的做法肯定是正确的
于是水之,1.4s卡过

全局最小割专门的算法还是很优越的样子
无论是时间复杂度或是编程复杂度
但我这个蒟蒻学不会QAQ
弃坑

#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=INF,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 = min(vout, 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); 
    printf("%d\n",vout);
    return 0;
}

【BZOJ 1123】[POI2008] BLO

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

就是求一求BCC
在求BCC的时候顺便统计一下答案

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

const int N = 100000+9;
const int M = 1000000+9;

int n,m,head[N],nxt[M],to[M],sz[N];
int lowlink[N],num[N],bcc_cnt,num_cnt; 
LL vout[N];
vector<int> bcc_num[N];
stack<int> stk;

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

void Tarjan(int w, int f) {
	lowlink[w] = num[w] = ++num_cnt;
	stk.push(w); sz[w] = 1; int tmp = 0;
	for (int i=head[w];i;i=nxt[i]) if (to[i] != f) {
		if (!num[to[i]]) {
			Tarjan(to[i], w);
			sz[w] += sz[to[i]];
			lowlink[w] = min(lowlink[w], lowlink[to[i]]);
			if (lowlink[to[i]] >= num[w]) {
				int cur; bcc_cnt++;
				do {
					cur = stk.top(); stk.pop();
					bcc_num[cur].push_back(bcc_cnt);
				} while (cur != to[i]);
				vout[w] += 2LL * tmp * sz[to[i]];
				tmp += sz[to[i]];
			}
		} else {
			lowlink[w] = min(num[to[i]], lowlink[w]);
		}
	}
	vout[w] += 2LL * tmp * (n - tmp - 1) + 2 * (n - 1);
}

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

int main(){
	n = read(); m = read();
	for (int i=1;i<=m;i++) {
		Add_Edge(read(),read());
	}
	Tarjan(1,1);
	for (int i=1;i<=n;i++) {
		printf("%lld\n",vout[i]);
	} 
	return 0;
}

【Codeforces 723E】One-Way Reform

题目传送门:http://codeforces.com/contest/723/problem/E
官方题解:http://codeforces.com/blog/entry/47502

出度与入度相等 => 欧拉回路
有度数为偶数的点 => 两两配对之后加一条边
此时所有点的度数都为偶数,每一个连通子图存在欧拉回路
于是可以用随便定向,只有用网络流来的得出可行解

现在唯一的问题就是证明度数为奇数的点一定是偶数对了:
一条边贡献两个度 => 总度数为偶数
若度数为奇数的点有奇数个 => 总度数为奇数
矛盾 => 度数为奇数的点不可能有奇数个

【BZOJ 2095】[Poi2010] Bridges

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

这个题目啊!瞄一眼就知道是二分吧?
接下来就是给你一些有向边&无向边,让你判断是否存在欧拉回路

对于有向边,没有悬念,直接记录对于出度入度的贡献
只与有向边嘛,不妨先随便定一个向,然后考虑使用网络流进行调整
具体细节可以参见:http://blog.csdn.net/wzq_qwq/article/details/48651379

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

const int N = 2000+9;
const int M = 10000+9;
const int INF = 1e9;

struct Edge{int u,v,w1,w2;}edge[M];
int n,m,MX,MN=INF,in[N],out[N],cur[M];
int S,T,dis[N],flow[M],head[N],nxt[M],to[M],TT; 
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) {
	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(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],tmp;i && f;i=nxt[i]) { 
			if (dis[to[i]] == dis[w] + 1) {
				tmp = DFS(to[i], min(f, flow[i]));
				ret += tmp; f -= tmp;
				flow[i] -= tmp; flow[i^1] += tmp;
			}
		}
		return ret;
	}
}

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

inline bool judge(int lim){
	memset(in,0,sizeof(in));
	memset(out,0,sizeof(out));
	memset(head,0,sizeof(head));
	S = 0; T = N - 1; TT = 1;
	int tot = 0;
	
	for (int i=1;i<=m;i++) {
		if (lim < edge[i].w1) {
			continue;
		} else if (lim < edge[i].w2) {
			in[edge[i].v]++;
			out[edge[i].u]++;
		} else {
			in[edge[i].v]++;
			out[edge[i].u]++;
			Add_Edge(edge[i].u, edge[i].v, 2);
		}
	}
	
	for (int i=1;i<=n;i++) {
		if (abs(in[i]-out[i]) & 1) {
			return false;
		} else if (in[i] < out[i]) {
			Add_Edge(S,i,out[i]-in[i]);	
			tot += out[i] - in[i];
		} else if (in[i] > out[i]) {
			Add_Edge(i,T,in[i]-out[i]);
		}
	}
	
	return Dinic() == tot;
}

int main(){
	n = read(); m = read();
	for (int i=1;i<=m;i++) {
		edge[i].u = read();
		edge[i].v = read();
		edge[i].w1 = read();	
		edge[i].w2 = read();
		if (edge[i].w1 > edge[i].w2) {
			swap(edge[i].w1, edge[i].w2);
			swap(edge[i].u, edge[i].v);
		}
		MX = max(MX, edge[i].w2);
		MN = min(MN, edge[i].w1);
	}
	
	int l = MN, r = MX, mid, ret = -1;
	while (l <= r) {
		mid = l + r >> 1;
		if (judge(mid)) ret = mid, r = mid - 1;
		else l = mid + 1;
	}
	if (~ret) printf("%d\n",ret);
	else puts("NIE");
	return 0;
}

【NOIP十连测】[D1T3] Walk

题目传送门:http://oi.cyo.ng/wp-content/uploads/2016/10/noip_test1_statements.pdf

唯一恶心的就是哪些根据val建的边
于是搞一个想分层图一样的玩意儿
然后有的边权为零,于是BFS的时候不往队首扔,往队尾扔即可

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

const int N = 1300000;
const int M = 200000*2+300000+9;

int n,m,head[N],nxt[M],to[M],dis[N];
deque<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) {
	static int T = 0;
	to[++T] = v; nxt[T] = head[u]; head[u] = T;
}

inline void BFS() {
	memset(dis,-1,sizeof(dis));
	dis[1] = 0; que.push_back(1);
	
	while (!que.empty()) {
		int w = que.front(); que.pop_front();
		for (int i=head[w];i;i=nxt[i]) 
			if (w <= n) {
				if (!~dis[to[i]] || dis[to[i]] > dis[w] + 1) dis[to[i]] = dis[w] + 1, que.push_back(to[i]);
			} else {
				if (!~dis[to[i]] || dis[to[i]] > dis[w]) dis[to[i]] = dis[w], que.push_front(to[i]);
			}
		if (w > n) for (int i=0;i<=20;i++) if ((w-n)&(1<<i)) {
			int v = ((w-n)^(1<<i)) + n; if (v <= n) continue;
			if (!~dis[v] || dis[v] > dis[w]) dis[v] = dis[w], que.push_front(v);
		} 
	}
}

int main(){
	freopen("walk.in","r",stdin);
	freopen("walk.out","w",stdout);
	n = read(); m = read();
	for (int i=1,tmp;i<=n;i++) tmp = read(), Add_Edge(i,n+tmp), Add_Edge(n+tmp,i);
	for (int i=1,u,v;i<=m;i++) u = read(), v = read(), Add_Edge(u,v);
	BFS(); for (int i=1;i<=n;i++) printf("%d\n",dis[i]);
	return 0;
}

【BZOJ 4514】[Sdoi2016] 数字配对

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

他都给你说了是匹配,那肯定是二分图匹配啦!
考虑可以整除,除完之后还是个质数,那么质因数的个数(不是种类)肯定是一奇一偶
于是建成二分图跑一跑MCMF()就行啦!
当然如果你比较刚烈,想跑带权带花树肯定也是可以的啦!

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

const int M = 200+9;
const int N = 100000+9;
const int MX = 100000; 
const int INF = 0x7fffffff;

int pri[N],tot,vis[N],n,A[M],B[M],C[M];
int lft[M],rit[M],tl,tr,sur[M],S,T,inq[M];
int head[M],nxt[N],to[N],flow[N];
LL cost[N],dis[M];
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 Get_Prime_Number(){
	for (int i=2;i<=MX;i++) {
		if (!vis[i]) pri[++tot] = i;
		for (int j=1;j<=tot&&i*pri[j]<=MX;j++) 
			if (i%pri[j]) vis[i*pri[j]] = 1;
			else {vis[i*pri[j]] = 1; break;}
	}
}

inline void Add_Edge(int u, int v, int f, LL 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 divide(int w) {int ret = 0; for (int i=1;i<=tot;i++) while (w%pri[i] == 0) w /= pri[i], ret++; return ret&1;}
inline bool is_prime(int w) {for (int i=1;i<=tot;i++) if (pri[i] >= w) break; else if (w%pri[i] == 0) return false; return true;}

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

inline LL MCMF(){
	LL ret = 0, tot_cost = 0; bool tag=1;
	for (int w=T,f;SPFA()&&tag;w=T) {
		for (f=INF;w!=S;w=to[sur[w]^1]) f = min(f, flow[sur[w]]); 
		if (dis[T]*f+tot_cost > 0) ret += -tot_cost / dis[T], tag = 0;
		else {
			for (w=T;w!=S;w=to[sur[w]^1]) flow[sur[w]] -= f, flow[sur[w]^1] += f;
			ret += f; tot_cost += f*dis[T];
		}
	} return ret;
}

int main(){
	n = read(); Get_Prime_Number(); S = 0; T = M - 1;
	for (int i=1;i<=n;i++) A[i] = read();
	for (int i=1;i<=n;i++) B[i] = read();
	for (int i=1;i<=n;i++) C[i] = read();
	for (int i=1;i<=n;i++) 
		if (divide(A[i])) lft[++tl] = i, Add_Edge(S,i,B[i],0);
		else rit[++tr] = i, Add_Edge(i,T,B[i],0);
	for (int i=1;i<=tl;i++) for (int j=1;j<=tr;j++) {
		int w1 = A[lft[i]], w2 = A[rit[j]]; if (w1 > w2) swap(w1, w2); 
		if (w2 % w1 == 0 && is_prime(w2 / w1)) 
			Add_Edge(lft[i],rit[j],INF,-1LL*C[lft[i]]*C[rit[j]]);
	} printf("%lld\n",MCMF());
	return 0;
}

【BZOJ 3832】[POI2014] Rally

相关链接:

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

解题报告

这题真的是妙不可言!
0MYHX~N~(WNGO)B6[N8ZL40
POI的题目质量真的还不错啊!

先DP预处理一下:
f[]表示顺着走能走多远
g[]表示反着走能走多远
对于边(u,v)给一个权值g[u]+f[v]
不难发现,一个图的最长链此时为权值最大的那一条边

考虑删点,如果会影响到最长链的话
新的最长链一定是从拓扑序小于他的连向拓扑序大于他的某条边的权值
于是搞一搞堆来维护这个东西即可

Code

代码方面,我偷懒写的set+map的写法
想要常数小,请参见:https://blog.sengxian.com/solutions/bzoj-3832

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

const int N = 500000+9;
const int M = 4000000+9; 
const int INF = 100000000;

int head[N],nxt[M],to[M],rhead[N],n,m,S,T;
int f[N],g[N],in[N],rin[N],vout=INF,Point;
struct CMP{inline bool operator () (const int a, const int b) {return b<a;}};
set<int,CMP> cur; unordered_map<int,int> CNT; queue<int> que;

inline void Add_Edge(int u, int v) {
	static int TT = 1; in[v]++; rin[u]++;
	to[++TT] = v; nxt[TT] = head[u]; head[u] = TT;
	to[++TT] = u; nxt[TT] = rhead[v]; rhead[v] = TT;
}

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

void solve(int w, int *frt, int *ret, int *cnt) {
	if (w != S && w != T) que.push(w);
	for (int i=frt[w];i;i=nxt[i]) {
		ret[to[i]] = max(ret[to[i]],ret[w]+1);
		if (!--cnt[to[i]]) solve(to[i],frt,ret,cnt);
 	}
}

int main(){
	n = read(); m = read(); S = 0; T = n+1;
	for (int i=1;i<=n;i++) Add_Edge(S,i), Add_Edge(i,T);
	for (int i=1,u,v;i<=m;i++) u = read(), v = read(), Add_Edge(u,v); 
	solve(S,head,f,in); solve(T,rhead,g,rin);
	for (int i=1;i<=n;++i) if (!CNT[g[i]]++) cur.insert(g[i]);
	for (int op=1;op<=n;op++) {
		int w = que.front(); que.pop(); 
		for (int i=rhead[w];i;i=nxt[i]) if (!--CNT[f[to[i]]+g[w]]) cur.erase(f[to[i]]+g[w]);
		if (vout > *(cur.begin())) vout = *(cur.begin()), Point = w; 
		for (int i=head[w];i;i=nxt[i]) if (!CNT[g[to[i]]+f[w]]++) cur.insert(g[to[i]]+f[w]);
	} printf("%d %d\n",Point,vout-1);
	return 0;
}

【NKOJ 2922】密码锁

题目传送门:http://42.247.7.121/zh/Problem/Details/2922
离线版题目:http://paste.ubuntu.com/23198035/
数据生成器:http://paste.ubuntu.com/23198038/

这题真的是好妙!点个大赞!
题解让我们召唤黄学长:http://hzwer.com/3308.html
另外,我的这份代码的复杂度多了一个k,要想速度快就去抄黄学长的代码吧!

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

int n,m,k,cnt,dis[10010],sz[200],pos[30],f[1200000],cost[30][30]; 
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 BFS(int W, int num) {
	memset(dis,127,sizeof(dis));
	que.push(W); dis[W] = 0;
	
	while (!que.empty()) {
		int w = que.front(); que.pop();
		for (int i=1;i<=m;i++) {
			if (w+sz[i] <= n+1 && dis[w+sz[i]] > dis[w]+1)  dis[w+sz[i]] = dis[w]+1, que.push(w+sz[i]);
			if (w-sz[i] >= 1 && dis[w-sz[i]] > dis[w]+1)  dis[w-sz[i]] = dis[w]+1, que.push(w-sz[i]);
		}
	} 
	for (int i=1;i<=cnt;i++) cost[num][i] = dis[pos[i]];
}

int main(){
	n = read(); k = read(); m = read();
	for (int i=1;i<=k;i++) dis[read()] = 1;
	for (int i=1;i<=m;i++) sz[i] = read(); sort(sz+1,sz+1+m); m = unique(sz+1,sz+1+m) - sz - 1;
	for (int i=1;i<=n+1;i++) if (dis[i] + dis[i-1] == 1) pos[++cnt] = i;
	for (int i=1;i<=cnt;i++) BFS(pos[i],i); 
	memset(f,127,sizeof(f));
	f[(1<<cnt)-1] = 0;
	for (int w=(1<<cnt)-1;w;w--) if (f[w] < 1e9) 
		for (int i=1;i<=cnt;i++) if (w&(1<<i-1)) for (int j=i+1;j<=cnt;j++) if (w&(1<<j-1) && cost[i][j] < 1e9)
			f[w^(1<<i-1)^(1<<j-1)] = min(f[w^(1<<i-1)^(1<<j-1)],f[w]+cost[i][j]); 
	printf("%d\n",f[0]>1e9?-1:f[0]);
	return 0;
}

【BZOJ 1061】[Noi2008] 志愿者招募

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

这题好神……
作为单纯形的例题,用费用流给艹过去……
题解的话,我已经膜拜得五体投地了,让我们召唤BYvoid:https://www.byvoid.com/blog/noi-2008-employee/

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

const int N = 1000+9;
const int M = 100000; 
const int INF = 1e9;

int head[N],nxt[M],to[M],flow[M],cost[M],dis[N];
int n,m,S,T,ned[N],inq[N],sur[N],FLOW[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, 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 int SPFA() {
    memset(dis,127,sizeof(dis));
    memset(FLOW,0,sizeof(FLOW));
    que.push(S); dis[S] = 0; inq[S] = 1; FLOW[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] && dis[w] + cost[i] < dis[to[i]]) {
            dis[to[i]] = dis[w] + cost[i]; sur[to[i]] = i;
            FLOW[to[i]] = min(FLOW[w], flow[i]);
            if (!inq[to[i]]) que.push(to[i]), inq[to[i]] = 1;
        }
    } return FLOW[T]; 
}
   
inline int MCMF() {
    int ret = 0;
    for (int w=T,f;f=SPFA();w=T) for (int t=sur[w];w!=S;t=sur[w]) 
        flow[t] -= f, flow[t^1] += f, ret += cost[t]*f, w = to[t^1];
    return ret;
}

int main(){
	n = read(); m = read(); S = 0; T = N-1;
	for (int i=1;i<=n;i++) ned[i] = read();
	for (int i=n+1;i;i--) ned[i] = ned[i-1] - ned[i];
	for (int i=1;i<=n+1;i++) if (ned[i] > 0) Add_Edge(S,i,ned[i],0); else Add_Edge(i,T,-ned[i],0);
	for (int i=1;i<=n;i++) Add_Edge(i,i+1,INF,0);  
	for (int i=1,s,t,c;i<=m;i++) s = read(), t = read(), c = read(), Add_Edge(t+1,s,INF,c);
	printf("%d\n",MCMF());
	return 0;
}

【BZOJ 2879】[Noi2012] 美食节

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2879
数据生成器:http://paste.ubuntu.com/23196968/
官方数据:http://pan.baidu.com/s/1nv4ginB

这题就是修车的升级版
除了动态加点,别无其他神奇的玩意儿
接下来请欣赏《论S与T的区别》:
看起来厨师拆开的点连到T,与连到S没什么区别
然而…….
98765454

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

const int L = 100+9;
const int N = 1000+9;
const int M = N*100;
const int INF = 100000000; 


int n,m,head[N],nxt[M],to[M],cost[M],dis[N],flow[M];
int inq[N],cnt,cur[N],mat[L][L],id[N],vout,S,T,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;
}

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,127,sizeof(dis));
	que.push(S); inq[S] = 1; dis[S] = 0;
	
	while (!que.empty()) {
		int w = que.front(); que.pop(); inq[w] = 0;
		for (int i=head[w];i;i=nxt[i]) if (flow[i] && dis[to[i]] > dis[w] + cost[i]) {
			dis[to[i]] = dis[w] + cost[i]; sur[to[i]] = i;
			if (!inq[to[i]]) que.push(to[i]), inq[to[i]] = 1;
		} 
	} return dis[T] < 1000000000;
}

inline void MCMF(){
	for (int nw;SPFA();) {
		for (int w=T;w!=S;w=to[sur[w]^1]) nw = id[w]?id[w]:nw, flow[sur[w]]--, flow[sur[w]^1]++;  
		id[++cnt] = nw; cur[nw]++; Add_Edge(S,cnt,1,0); vout += dis[T]; 
		for (int j=1;j<=n;j++) Add_Edge(cnt,j,1,mat[j][nw]*cur[nw]); 
 	}
}

int main(){
	n = read(); m = read(); S = 0; T = N - 1; cnt = n+m;
	for (int i=1,tmp;i<=n;i++) Add_Edge(i,T,tmp=read(),0);
	for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) mat[i][j] = read();
	for (int i=1;i<=m;i++) {
		id[i+n] = i; cur[i] = 1; Add_Edge(S,i+n,1,0);
		for (int j=1;j<=n;j++) Add_Edge(i+n,j,1,mat[j][i]);
	} MCMF(); printf("%d\n",vout);
	return 0;
}

【BZOJ 1458】士兵占领

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

大体上同BZOJ_1711
具体来说,假设全部填上,于是要删尽量多的点
然后行放左边,列放右边,点拆开放中间

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

const int L = 100+9;
const int N = 50000+9;
const int M = 1000000;
const int INF = 1000000000;

int head[N],nxt[M],to[M],dis[N],cur[N],flow[M];
int n,m,X[L],Y[L],cx[L],cy[L],k,S,T,mat[L][L],vout;
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(){
	m = read(); n = read(); k = read(); S = 0, T = N-1; vout = n*m;
	for (int i=1;i<=m;i++) Y[i] = read(), cy[i] = n;
	for (int i=1;i<=n;i++) X[i] = read(), cx[i] = m;
	for (int i=1,x,y;i<=k;i++) x = read(), y = read(), mat[x][y] = 1, cx[x]--, cy[y]--, vout--;
	for (int i=1;i<=m;i++) if (cy[i] < Y[i]) puts("JIONG!"), exit(0);
	for (int i=1;i<=n;i++) if (cx[i] < X[i]) puts("JIONG!"), exit(0);
	
	for (int i=1;i<=n;i++) Add_Edge(S,i,cx[i]-X[i]);
	for (int i=1;i<=m;i++) Add_Edge(n+i,T,cy[i]-Y[i]);
	for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) if (!mat[i][j]) 
		Add_Edge(m+n+id(i,j),m+n+m+n+id(i,j),1),
		Add_Edge(i,m+n+id(i,j),1), Add_Edge(m+n+m+n+id(i,j),j+n,1);
	printf("%d\n",vout-Dinic());
	return 0;
}