[树链剖分][最小割]

orz zcg

给出一棵树,n<=10^6,有一些询问,每次求(u, v)的LCA

树剖卡内存也可以这么写啊。。

然而这样的做法会被卡,如果一条链每一个甩出一个子节点就会被卡。

还有sdoi2016那道游戏,贪心更新标记即可。

#include <cstdio>
#define maxn 1000001

int n, fa[maxn], top[maxn];

int main(){
	freopen("squirrel.in", "r", stdin);
	freopen("squirrel.out", "w", stdout);
	int test, x;
	scanf("%d%d", &n, &test); top[1] = -1;
	for(int i = 2; i <= n; i ++){
		scanf("%d", &x);
		fa[i] = x;
		if(top[x] < 0)top[i] = top[x], top[x] = -top[x];
		else top[i] = -i;
	}
	for(int i = 1; i <= n; i ++)
		if(top[i] < 0) top[i] = -top[i];
	int u, v;
	for(int i = 1; i <= test; i ++){
		scanf("%d%d", &u, &v);
		while(top[u] != top[v]){
			if(fa[top[u]] < fa[top[v]])
			    u ^= v ^= u ^= v;
			u = fa[top[u]];
		}
		printf("%d\n", u < v ? u : v);
	}
	
	return 0;
}

 2. 百步穿杨
(archerycpp/c/pas)
【问题描述】
Star 是一个神箭手,不过并不是他自己射箭——在一块N * M 的方格空地上有些地方摆有箭塔,其它还有地方有箭靶。每个箭靶有不同的分值,而箭塔的方向为上下左右其中的一种,Star 通过控制箭塔选定目标,然后一起发射击中这些箭靶获取其分数来展现自己的“射箭”技术。现在你需要计算在给定的空地上,Star 最多能获得多大的分数。
相关的限制如下:

1、每个箭塔原则上可以选择所指向方向上任意一个箭靶,当然也可以选择不攻击;
2、为保证正常运行,每个箭塔的攻击范围中不会有其它箭塔;
3、同样为确保正常攻击,所有箭塔的攻击轨迹在地面的投影不能在任意格
子相交。

【输入】
第一行两个整数N、M;
接下来N 行每行M 个字符,表示每个格子的状态:
1、“.”表示空地;
2、“A”、“V”、“<”、“>”分别表示朝向上下左右的箭塔;
3、“1”~“9”分别表示得分为1 到9 的箭靶。
【输出】
输出一行一个整数,表示最大得分。
【输入输出样例】
archery.in archery.out
3 4
.9V.
>..7
.A1.
10
【数据范围】
对于30%的数据,N, M ≤ 5;
对于100%的数据,N, M ≤ 50。

 

一眼最小割(然而并没有什么卵用)

看错题了啊QAQ。。任意一个。。

这个建图好神奇。。

根据数据范围,我们想办法把原问题转化为最小割。我们把每个格子i 拆成两个点i、i',分别表示这个格子被横向或纵向的轨迹穿过。不妨设i 与S 相连表示纵向穿过、i'与T 相连表示横向穿过。由于求最小割,我们首先贪心出最大收益——每个箭塔均取它发射方向上的最大值(忽略其它一切限制)。我们要“割”的正是不合法情况,考察有哪些限制:

①一个点不能同时取纵向与横向;
②箭塔所在格不能取和它本身不同的方向;
③顺着一个箭塔的轨迹往下走,若轨迹在去向某些格子处停止,则要付出该箭塔最大值减当前收益的代价。

以上限制均转化为最小割的建边,然后跑网络流即可。
O(Maxflow(N2, N3))。

这样的图竟然不到0.1ms就过了

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>

#define maxn 51
using namespace std;

char a[maxn][maxn];

int n, m, S, T;
//---------------------------------------------------------------------//
struct Edge{
	int to, next, w, dis;
	Edge(int v = 0, int nxt = 0, int w = 0):
		to(v), next(nxt), w(w){}
}edge[5000010];

int h[100010], cnt = 1;
void add(int u, int v, int w){
	edge[++ cnt] = Edge(v, h[u], w); h[u] = cnt;
	edge[++ cnt] = Edge(u, h[v], 0);h[v] = cnt;
}

queue<int> Q;
int d[100010];
const int inf = 0x7fffffff / 2;

bool BFS(){
	Q.push(S);
	for(int i = 1; i <= T; i ++)
	    d[i] = -1;
	d[S] = 0;
	while(!Q.empty()){
		int u = Q.front(); Q.pop();
		for(int i = h[u]; i; i = edge[i].next){
			if(edge[i].w == 0)continue;
			int v = edge[i].to;
			if(d[v] == -1)d[v] = d[u] + 1, Q.push(v);
		}
	}return d[T] != -1;
}

int DFS(int x, int a){
	if(a == 0 || x == T)
	    return a;
	int used = 0, f;
	for(int i = h[x]; i; i = edge[i].next){
		int v = edge[i].to;
		if(d[v] == d[x] + 1){
			f = DFS(v, min(edge[i].w, a - used));
			used += f;
			edge[i].w -= f;
			edge[i^1].w += f;
			if(used == a)return used;
		}
	}
	if(!used)d[x] = -1;
	return used;
}

int Max_Flow(){
	int ret = 0;
	while(BFS())
	    ret += DFS(S, inf);
	return ret;
}
//---------------------------------------------------------------------//


int in[maxn][maxn], out[maxn][maxn];

const int dx[] = {0, -1, 1, 0, 0};
const int dy[] = {0, 0, 0, 1, -1};
const char ch[] = " AV><";
int M[500];


int main(){
	freopen("archery.in", "r", stdin);
	freopen("archery.out", "w", stdout);
	M['A'] = 1, M['V'] = 2, M['>'] = 3, M['<'] = 4;
	memset(a, 0, sizeof a);
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++){
		scanf("%s", a[i] + 1);
		a[i][m+1] = 0;
	}

	int idf = 0 ;
	for(int i = 1; i <= n; i ++)
	    for(int j = 1; j <= m; j ++){
			in[i][j] = ++ idf, out[i][j] = ++ idf;
			add(in[i][j], out[i][j], inf);
	    }
	        
    int ans = 0, dir, x, y, t;
    S = 0, T = ++ idf;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++){
			if(dir = M[a[i][j]]){
				if(dir < 3)add(S, in[i][j], inf);
				else add(out[i][j], T, inf);
				for(t = 0, x = i, y = j; in[x][y]; x += dx[dir], y += dy[dir])
				    if(a[x][y] >= '0' && a[x][y] <= '9')
				        t = max(t, a[x][y] ^ 48);
				ans += t;
				x = i + dx[dir], y = j + dy[dir];
				if(a[x][y]){
					if(dir < 3)add(in[i][j], in[x][y], t);
					else add(out[x][y], out[i][j], t);
					for(; in[x+dx[dir]][y+dy[dir]]; x += dx[dir], y += dy[dir]){
						int w = (a[x][y] >= '0' && a[x][y] <= '9') ? t - a[x][y] + 48: t;
						if(dir < 3)add(in[x][y], in[x+dx[dir]][y+dy[dir]], w);
						else add(out[x+dx[dir]][y+dy[dir]], out[x][y], w);
					}
				}
			}
        }
        
	printf("%d\n", ans - Max_Flow());
	return 0;
}

 另外一道求最小割的神题

勇闯法法塔(tower)

【题目描述】

法法塔位于法法城西南,共有5层,是战狂用来发动大型魔法的武器。飞刀杂耍者和小象引开守卫后,小奇和蛤布斯潜入了法法塔。

法法塔第1层左侧有一个n*m的魔法阵,魔法阵的每一格刻有“#”或“.”。蛤布斯发现,要破解魔法阵,需要对每个“#”施放炎爆术,但不能攻击到“.”。蛤布斯法力高超,每次可以同时对一行(列)的连续一段格子施放炎爆术。

为了尽快破解魔法阵,蛤布斯希望你帮他求出他最少需要施放多少次炎爆术。

【读入数据】

n行,每行一个长度为m的字符串。

【读出数据】

      一行一个数表示答案。

【样例读入】

.##.####.####.#########.##..

##.#.####################.##

.##.###.##.###.###.###.###..

#..###..#########..###.##.##

####..#######.#.#####.######

##.######.#..#.#############

##.######.###########.######

#######.#######.#..###.#.###

#####..#######.#####.#.###.#

#..#################.#.####.

##.######..#.#####.######.##

..#.#############.#.##....#.

....##..##..#.#####.#.###.##

##.#########...#..#.#.######

##.#..###########..#..####.#

#.####.###.########.########

#####.#########.##.##.######

.##.####..###.###...######.#

【样例输出】

88

 

 

Sol:

问题等价于给每个格子赋上“横”或“竖”,代价就是(赋上的方向下一格是墙的格子个数+方向不同的相邻格子对数)/2。跑一下最小割就行了。

时间复杂度O(maxflow(nm,nm))

我们可以考虑如果一个点属于S集代表选择横向,属于T集代表选择纵向,那么选择横向时我们要把这个点连接T并且代价为这个点横向两边是否是'.'。纵向同理,当两个相邻的格子方向不同时代价为1。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

#define maxn 51
#define maxnode 5010

using namespace std;
const int inf = 0x7fffffff;
char s[maxn][maxn];
int n, m, idfclock, id[maxn][maxn];

struct Edge{int to, nxt, w;}edge[2000010];
int h[maxnode], cnt = 1;
void add(int u, int v, int w){
	if(w == 0)return;
	edge[++ cnt] = (Edge){v, h[u], w}; h[u] = cnt;
	edge[++ cnt] = (Edge){u, h[v], 0}; h[v] = cnt;
}

int S, T, d[maxnode], que[maxnode];

bool BFS(){
	memset(d, -1, sizeof d);
	d[S] = 0; int head = 0, tail = 0;
	que[tail ++] = S;
	while(head != tail){
		int u = que[head ++];
		for(int i = h[u]; i; i = edge[i].nxt){
			if(edge[i].w == 0)continue;
			int v = edge[i].to;
			if(d[v] == -1)d[v] = d[u] + 1, que[tail ++] = v;
		}
	}return d[T] != -1;
}

int DFS(int x, int a){
	if(x == T || a == 0)return a;
	int used = 0, f;
	for(int i = h[x]; i; i = edge[i].nxt){
		int v = edge[i].to;
		if(d[v] == d[x] + 1){
			f = DFS(v, min(edge[i].w, a - used));
			edge[i].w -= f;
			edge[i ^ 1].w += f;
			used += f;
			if(used == a)return used;
		}
	}
	if(used == 0)d[x] = -1;
	return used;
}

int Dinic(){
	int ret = 0;
	while(BFS())
		ret += DFS(S, inf);
	return ret;
}

bool check(int x, int y){
	return x > 0 && y > 0 && x <= n && y <= m && s[x][y] == '#';
}

const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};

int main(){
	freopen("tower.in", "r", stdin);
	freopen("tower.out", "w", stdout);
	while(~ scanf("%s", s[++ n] + 1));
	n --, m = strlen(s[1] + 1);
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= m; j ++)
			if(s[i][j] == '#')id[i][j] = ++ idfclock;
    int ans = idfclock;
	S = 0, T = ++ idfclock;
	for(int i = 1; i <= n; i ++){
		for(int j = 1; j <= m; j ++){
			if(s[i][j] == '#'){
				add(id[i][j], T, (!check(i, j - 1)) + (!check(i, j + 1)));
				add(S, id[i][j], (!check(i - 1, j)) + (!check(i + 1, j)));
				for(int k = 0; k < 4; k ++){
					int x = i + dx[k], y = j + dy[k];
					if(check(x, y))add(id[i][j], id[x][y], 1);
				}
			}
		}
	}
	
	printf("%d\n", Dinic() / 2);
	return 0;
}

选数
【问题描述】
有n 个正整数对,其中第i 个数对为(ai, bi)。请你选出一些数对,使得选出
的任意两个数对(记为第i 对和第j 对)满足以下两个条件之一:
1. 不存在正整数T 使得ai ^ 2 + aj ^ 2 = T ^2
2. ai 和aj 有大于1 的公因子。
选择第i 个数对带来的收益为bi。求收益总和的最大值。

 

Sol:最小割

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define maxn 2010
#define In(x) x
#define Out(x) x + n

using namespace std;
typedef long long ll;
const int inf = 0x7fffffff;

int n, S, T, a[maxn], b[maxn];

ll hs[2000010];
int h[maxn], cnt = 1;
struct Edge{int to, nxt, w;}edge[2000010];
void add(int u, int v, int w){
	edge[++ cnt] = (Edge){v, h[u], w}; h[u] = cnt;
	edge[++ cnt] = (Edge){u, h[v], 0}; h[v] = cnt;
}

int gcd(int a, int b){return !b ? a : gcd(b, a % b);}

int que[maxn], d[maxn];
bool BFS(){
	int head = 0, tail = 0;
	memset(d, -1, sizeof d);
	d[S] = 0, que[tail ++] = S;
	while(head != tail){
		int u = que[head ++];
		for(int i = h[u]; i; i = edge[i].nxt){
			if(edge[i].w == 0)continue;
			int v = edge[i].to;
			if(d[v] == -1)d[v] = d[u] + 1, que[tail ++] = v;
		}
	}return d[T] != -1;
}

int DFS(int x, int a){
	if(x == T || a == 0)return a;
	int used = 0, f;
	for(int i = h[x]; i; i = edge[i].nxt){
		int v = edge[i].to;
		if(d[v] == d[x] + 1){
			f = DFS(v, min(a - used, edge[i].w));
			edge[i].w -= f;
			edge[i ^ 1].w += f;
			used += f;
			if(a == used)return used;
		}
	}
	if(used == 0)d[x] = -1;
	return used;
}

int Dinic(){
	int ret = 0;
	while(BFS())
	    ret += DFS(S, inf);
	return ret;
}

int main(){
	freopen("number.in", "r", stdin);
	freopen("number.out", "w", stdout);
	scanf("%d", &n);
	int mx = 0;
	for(int i = 1; i <= n; i ++){
		scanf("%d", &a[i]);
		mx = max(mx, a[i]);
	}
	
	mx *= 2;
	for(int i = 1; i <= n; i ++)
		scanf("%d", &b[i]);
	
	for(int i = 1; i <= mx; i ++)
		hs[i] = (ll)i * i;
		
	int ans = 0;
	S = 0, T = 2 * n + 1;
	for(int i = 1; i <= n; i ++){
		ans += b[i];
		add(S, In(i), b[i]), add(Out(i), T, b[i]);
		for(int j = i + 1; j <= n; j ++){
			if(gcd(a[i], a[j]) == 1){
				ll nw = (ll)a[i] * a[i] + (ll)a[j] * a[j];
				if(*lower_bound(hs + 1, hs + 1 + mx, nw) == nw)
				    add(In(i), Out(j), inf), add(In(j), Out(i), inf);
			}
		}
	}
	
	printf("%d\n", ans - Dinic() / 2);
	return 0;
}

  

求一个0,1,2的图中选出L型字符的最大个数  

posted @ 2016-04-13 14:59  _Horizon  阅读(295)  评论(0)    收藏  举报