[树链剖分][最小割]
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型字符的最大个数

浙公网安备 33010602011771号