关于一些伪树上dp(贪心)

前言:最近做了好多树上的类似dp的东西,今天是整理正好做个总结

共同点:都是一种可以说是贪心的思想,能在子树中解决的都在子树中解决,解决不了的交给该子树的父节点的那颗子树中解决

题目概述:
这题太虎了,所以没有背景。
给你一棵树,边有黑白两种颜色,你每次可以选择两个点,把这两个点之间的唯一简单路径上的所有边颜色取反,某些边要求最终颜色必须是黑色,还有些边没有要求,问最少操作多少次能达到目的
第一行一个整数n,代表点数
接下来 (n - 1) 行,每行三个数 (x, y, z),代表点 (i)(x) 之间有一条边,若 (y) 为 0 代表初始为白色,否则为黑色,若 (z) 为 0 代表不对最终颜色做要求,否则代表要求为黑色。

思路

很显然的对于一个子树跟节点而言,与它相连的所有的边(通向子节点),要是不做要求,那么有可能会被翻转,要是要求变黑并且原来是白色的,那么一定不会被翻转,如果一开始就是黑色的,那么一定不翻转。
考虑哪些情况在子树中找不到配对的可以上传父节点:
1:该子树与父节点的连边原本就需要翻转
2:该子树与父节点的连边可以翻转也可以不翻转(不做要求)
然后我们就dfs解决就好了

代码实现

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e6 + 50;
inline int read () {
	int x = 0, f = 1; char ch = getchar();
	for (;!isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	return x * f;
}
int n;
struct Edge {
	int to, next, judge, special;
} edge[maxn << 1];
int tot, head[maxn];
void addedge (int a, int b, int col, int spc) {
	edge[++tot].to = b;
	edge[tot].next = head[a];
	head[a] = tot;
	if (col == 1) {
		edge[tot].judge = 1;
	} else if (spc == 1) {
		edge[tot].special = 1;
	}
}
int siz[maxn];
int cnt;
int fa[maxn];
void dfs (int u, int f, int opt, int judge) {
	for (register int i = head[u]; i; i = edge[i].next) {
		int v = edge[i].to;
		if (v == f) continue;
		fa[v] = u;
		if (edge[i].judge == 1) {
			siz[u]++;
		}
		if (edge[i].judge == 1) {
			dfs (v, u, 1, 0);
		} else if (edge[i].special == 1) {
			dfs (v, u, 0, 1);
		} else {
			dfs (v, u, 0, 0);
		}
	}
	cnt += siz[u] / 2;
	siz[u] %= 2;
	if (siz[u] == 0) {
	} else if (siz[u] == 1 && opt == 1) {
	} else if (opt == 0 && judge == 1){
		siz[fa[u]]++;
	} else if (siz[u] == 1 && opt == 0 && judge == 0) {
		cnt++;
	}
}
int main () {
	n = read();
	int x, y, z;
	for (register int i = 2; i <= n; i++) {
		x = read(), y = read(), z = read();
		if (z == 0) {
			addedge (i, x, 0, 1);
			addedge (x, i, 0, 1);
		} else if (y == 0 && z != 0) {
			addedge (i, x, 1, 0);
			addedge (x, i, 1, 0);
		} else {
			addedge (i, x, 0, 0);
			addedge (x, i, 0, 0);
		}
	}
	dfs (1, 0, 0, 0);
	cout<<cnt<<endl;
	return 0;
}

Graph

给定一张 (n) 个点, m条边的无向图,每条边连接两个顶点,保证无重边自环,不保证联通。
你想在这张图上进行若干次旅游,每次旅游可以任选一个点作为起点,再走到一个与 (x) 直接有边相连的点 (y) , 再走到一个与 (y) 有边相连的点 (z) 并结束本次旅游。
作为一个旅行爱好者,你不希望任意一条边超过一次, 注意一条边不能正向走一次,反向走一次,点可以经过多次,满足该情况你希望尽可能多次的旅游,请计算出最多的旅行次数,并任意输出一种方案。

思路

很显然对于一个联通块来说答案就是边数 % 3, 你就可以发现一种最有的贪心策略,可以在字数中找到配对的就在子树中解决,找不到的就传到父节点中,在父节点找配对的边,注意要优先解决这种在子树中找不到配对的边,因为如果上传父节点之后还是没有配对,那么这条边就不可以再次上传了,并且这种边一定可以在上传父节点时找到配对。
(vector) 随便维护一下就好了

代码实现

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 1e5 + 50;
inline int read () {
	int x = 0, f = 1; char ch = getchar();
	for (;!isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	return x * f;
}
int n, m;
struct Edge {
	int from, to, next;
	bool judge;
} edge[maxn << 2];
int tot, head[maxn];
void addedge (int a, int b) {
	edge[++tot].to = b;
	edge[tot].from = a;
	edge[tot].next = head[a];
	head[a] = tot;
}
bool vis[maxn];
int cnt;
struct Node {
	int fro, midd, too;
}node[maxn];
vector<pair<int, int> > vec[maxn];
int fa[maxn];
void dfs (int u) {
	vis[u] = true;
	for (register int i = head[u]; i; i = edge[i].next) {
		int v = edge[i].to;
		if (edge[i].judge == true) continue;
		edge[i].judge = true;
		if (edge[i + 1].from == edge[i].to && edge[i + 1].to == edge[i].from) {
			edge[i + 1].judge = true;
		} else {
			edge[i - 1].judge = true;
		}
		if (vis[v]) {
			vec[u].push_back(make_pair(u, v));
			continue;
		}
		fa[v] = u;
		dfs (v);
		vec[u].push_back(make_pair(u, v));
	}
	int siz = vec[u].size();
	for (register int i = 0; i < vec[u].size(); i++) {
		int x = vec[u][i].first, y = vec[u][i].second;
		if (x != u && y != u){
			int xx = vec[u][i + 1].first, yy = vec[u][i + 1].second;
			if (xx == x) {
				node[++cnt] = (Node) {yy, xx, y};
			} else if (yy == x) {
				node[++cnt] = (Node) {xx, yy, y};
			} else if (xx == y){
				node[++cnt] = (Node) {x, xx, yy};
			} else {
				node[++cnt] = (Node) {x, yy, xx};
			}
			i++;
		} else if (i + 1 < siz) {
			int xx = vec[u][i + 1].first, yy = vec[u][i + 1].second;
			if (xx == u) {
				node[++cnt] = (Node) {y, x, yy};
				i++;
			}
			else {
				vec[u].push_back(make_pair(x, y));
				siz++;
			}
		} else {
			vec[fa[u]].push_back(make_pair(x, y));
		}
	}
}
int main () {
	n = read(), m = read();
	int x, y;
	for (register int i = 1; i <= m; i++) {
		x = read(), y = read();
		addedge (x, y), addedge (y, x);
	}
	for (register int i = 1; i <= n; i++) {
		if (vis[i] == false) dfs(i);
	}
	cout<<cnt<<endl;
	for (register int i = 1; i <= cnt; i++) {
		printf("%d %d %d
", node[i].fro, node[i].midd, node[i].too);
	}
	return 0;
}

凉宫春日的消失

在观察凉宫和你相处的过程中, (Yoki) 产生了一个叫做爱的 (bugfeature) ,将自己变成了一个没有特殊能力的普通女孩并和你相遇。但你仍然不能扔下凉宫,准备利用 (Yoki) 留下的紧急逃脱程序回到原来的世界。这个紧急逃脱程序的关键就是将线索配对。
为了简化问题,我们将可能的线索间的关系用一棵n个点的树表示,两个线索的距离定义为其在树上唯一最短路径的长度。因为你不知道具体的线索是什么,你需要进行 (q) 次尝试,每次尝试都会选中一个大小为偶数的线索集合 (V) ,你需要将线索两两配对,使得配对线索的距离之和不超过 (n) 。如果这样的方案不存在,输出 (No)

思路

一眼看到选关键点,显然可以用虚树搞,并且很显然有一个性质,该条件只要关键点数是偶数,那么一定存在方案。一个类似贪心的思想,可以在一颗子树中找到配对的就在一颗子树中解决,并且一颗子树中最多只会有一个点没有找到配对,那么把当前点扔到父节点中找配对,并且这个点选最靠上的

代码实现

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 2e5 + 50;
inline int read () {
    int x = 0, f = 1; char ch = getchar();
    for (;!isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
    for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
    return x * f;
}
int n;
struct Edge {
    int from, to, next;
} edge[maxn << 1];
int tot, head[maxn];
inline void addedge (int a, int b) {
    edge[++tot].to = b;
    edge[tot].from = a;
    edge[tot].next = head[a];
    head[a] = tot;
}
deque<int> que[maxn];
bool col[maxn];
int f[maxn];
int son[maxn], siz[maxn], deep[maxn];
void dfs1 (int u) {
    siz[u] = 1;
    for (register int i = head[u]; i; i = edge[i].next) {
        int v = edge[i].to;
        if (v == f[u]) continue;
        f[v] = u;
        deep[v] = deep[u] + 1;
        dfs1 (v);
        siz[u] += siz[v];
        if (siz[v] > siz[son[u]]) son[u] = v;
    }
}
int dfn[maxn], dfn_clock;
int top[maxn];
void dfs2 (int u) {
    dfn[u] = ++dfn_clock;
    if (son[u]) {
        top[son[u]] = top[u];
        dfs2 (son[u]);
        for (register int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].to;
            if (v != f[u] && v != son[u]) {
                top[v] = v;
                dfs2 (v);
            }
        }
    }
}
inline int lca (int x, int y) {
    while (top[x] != top[y]) {
        if (deep[top[x]] > deep[top[y]]) {
            x = f[top[x]];
        } else {
            y = f[top[y]];
        }
    }
    if (deep[x] < deep[y]) return x;
    return y;
}
int tp;
int stk[maxn];
inline void ins(int x)
{
    if (tp == 0)
    {
        stk[tp = 1] = x;
        return;
    }
    int ance = lca(stk[tp], x);
    while ((tp > 1) && (deep[ance] < deep[stk[tp - 1]]))
    {
        addedge(stk[tp - 1], stk[tp]);
        --tp;
    }
    if (deep[ance] < deep[stk[tp]]) addedge(ance, stk[tp--]);
    if ((!tp) || (stk[tp] != ance)) stk[++tp] = ance;
    stk[++tp] = x;
}
int a[maxn];
inline void dfs (int u) {
	stk[++tp] = u;
    for (register int i = head[u]; i; i = edge[i].next) {
        int v = edge[i].to;
        dfs(v);
        if (!que[v].empty()) {
            int a = que[v].front();
            que[v].pop_front();
            que[u].push_back(a);
        }
    }
    if (col[u]) que[u].push_front(u);
    while (!que[u].empty()) {
        int a = que[u].back();
        que[u].pop_back();
        if (!que[u].empty()) {
            int b = que[u].back();
            que[u].pop_back();
            printf("%d %d
", a, b);
        } else {
            que[u].push_back(a);
            break;
        }
    }
}
bool cmp (int a, int b) {
    return dfn[a] < dfn[b];
}
int main () {
    n = read();
    int x, y;
    for (register int i = 1; i < n; i++) {
        x = read(), y = read();
        addedge (x, y), addedge (y, x);
    }
    int s;
    dfs1 (1);
    dfs2 (1);
    memset (head, 0, sizeof head);
    tot = 0;
    while (1) {
        s = read();
        if (s == 0) return 0;
        for (register int i = 1; i <= s; i += 1) {
            a[i] = read();
            col[a[i]] = true;
        }
        printf("Yes
");
        sort (a + 1, a + 1 + s, cmp);
        if (a[1] != 1) {
            stk[tp = 1] = 1;
        }
        for (register int i = 1; i <= s; i++) {
            ins (a[i]);
        }
        if (tp) {
            while (--tp) {
                addedge (stk[tp], stk[tp + 1]);
            }
        }
        tp = 0;
        dfs (1);
        for (register int i = 1; i <= tp + 1; i++) {
        	head[stk[i]] = 0;
        	col[stk[i]] = false;
        }
        tp = 0;
        tot = 0;
    }
    return 0;
}

完结散花(QwQ), (csp rp++)

原文地址:https://www.cnblogs.com/hzoi-liujiahui/p/13800890.html