[洛谷4643]【模板】动态dp 题解
题目地址:ė …
May all the beauty be blessed.
题目地址:Codeforces:Problem – 916E – Codeforces、洛谷:【CF916E】Jamie and Tree – 洛谷
To your surprise, Jamie is the final boss! Ehehehe.
Jamie has given you a tree with n vertices, numbered from 1 to n. Initially, the root of the tree is the vertex with number 1. Also, each vertex has a value on it.
Jamie also gives you three types of queries on the tree:
1 v — Change the tree’s root to vertex with number v.
2 u v x — For each vertex in the subtree of smallest size that contains u and v, add x to its value.
3 v — Find sum of values of vertices in the subtree of vertex with number v.
A subtree of vertex v is a set of vertices such that v lies on shortest path from this vertex to root of the tree. Pay attention that subtree of a vertex can change after changing the tree’s root.
Show your strength in programming to Jamie by performing the queries accurately!
给你一棵有根树,最开始根是1,点上有点权,三种操作:
输入格式:
The first line of input contains two space-separated integers n and q (1 ≤ n ≤ 10^5, 1 ≤ q ≤ 10^5) — the number of vertices in the tree and the number of queries to process respectively.
The second line contains n space-separated integers a1, a2, …, an ( - 10^8 ≤ ai ≤ 10^8) — initial values of the vertices.
Next n - 1 lines contains two space-separated integers ui, vi (1 ≤ ui, vi ≤ n) describing edge between vertices ui and vi in the tree.
The following q lines describe the queries.
Each query has one of following formats depending on its type:
1 v (1 ≤ v ≤ n) for queries of the first type.
2 u v x (1 ≤ u, v ≤ n, - 10^8 ≤ x ≤ 10^8) for queries of the second type.
3 v (1 ≤ v ≤ n) for queries of the third type.
All numbers in queries’ descriptions are integers.
The queries must be carried out in the given order. It is guaranteed that the tree is valid.
输出格式:
For each query of the third type, output the required answer. It is guaranteed that at least one query of the third type is given by Jamie.
输入样例#1:
6 7 1 4 2 8 5 7 1 2 3 1 4 3 4 5 3 6 3 1 2 4 6 3 3 4 1 6 2 2 4 -5 1 4 3 3
输出样例#1:
27 19 5
输入样例#2:
4 6 4 3 5 6 1 2 2 3 3 4 3 1 1 3 2 2 4 3 1 1 2 2 4 -3 3 1
输出样例#2:
18 21
The following picture shows how the tree varies after the queries in the first sample.
雅礼集训Day 1 Prob 1,然而并没能A掉。
换根用LCT做比较快,而子树和用DFS序线段树做比较快。我们要从里面选择一种方法实现。
LCT做的话,需要用到维护子树(包含虚边连的子树)信息的LCT,还要用LCT实现找LCA的算法。具体做法是,找access u和access v路径上最深的相同点。
用线段树做会好写一些,换根对子树加法求和的影响实际上可以讨论出来。试想一条现在根(即1)到换根后的根的路径,对于子树加法的操作,如果u到v的路径与当前根到1的路径无交集时,换根对它产生不了影响,例如样例1中,换根到6后的u=4, v=5这个点对就属于该种情况,这种情况的判定是u和v在根为1的树(下称原树)的LCA与现在的根再求一次LCA,若得到的结果不是原来的LCA,说明属于此种情况;反之,我们需要找到两条路径交集中最深的那个点,可以在LCA(u, rt)和LCA(v, rt)中取最深点,这个点在换根后的树上是最浅的点,子树加法时,只需要加这个点上面的那部分即可,即全树加x,再在当前根到1的路径上的这个点的子节点对应的子树处-x即可,查询同理加全树减这一部分权值即可。
需要保证操作是严格$O(\log n)$的否则会被卡,复杂度$O(n \log n)$。
// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <vector>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1; register char c = fgc();
for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
for(; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
return res * neg;
}
const int MAXN = 100005;
std::vector<int> gra[MAXN];
int n, q, a[MAXN];
int fa[MAXN], siz[MAXN], son[MAXN], dep[MAXN];
inline void dfs1(int u) {
siz[u] = 1;
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa[u]) continue;
fa[v] = u; dep[v] = dep[u] + 1;
dfs1(v); siz[u] += siz[v];
if(siz[v] > siz[son[u]]) son[u] = v;
}
}
int top[MAXN], dfn[MAXN], vn[MAXN], clk;
inline void dfs2(int u, int tp) {
dfn[u] = ++clk; vn[dfn[u]] = u; top[u] = tp;
if(son[u]) dfs2(son[u], tp);
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
inline int querylca(int u, int v) {
int tu = top[u], tv = top[v];
while(tu != tv) {
if(dep[tu] > dep[tv]) {
std::swap(u, v); std::swap(tu, tv);
}
v = fa[tv]; tv = top[v];
}
if(dep[u] < dep[v]) return u; else return v;
}
LL sum[MAXN << 2], tag[MAXN << 2];
#define lch o << 1
#define rch o << 1 | 1
#define mid ((l + r) >> 1)
inline void build(int o, int l, int r) {
if(l == r) {
sum[o] = a[vn[l]]; return;
}
build(lch, l, mid);
build(rch, mid + 1, r);
sum[o] = sum[lch] + sum[rch];
}
inline void pushdown(int o, int l, int r) {
if(tag[o]) {
tag[lch] += tag[o]; tag[rch] += tag[o];
sum[lch] += tag[o] * (mid - l + 1); sum[rch] += tag[o] * (r - mid);
tag[o] = 0;
}
}
inline void add(int o, int l, int r, int ll, int rr, LL v) {
if(l >= ll && r <= rr) {
sum[o] += v * (r - l + 1); tag[o] += v; return;
}
pushdown(o, l, r);
if(ll <= mid) add(lch, l, mid, ll, rr, v);
if(rr > mid) add(rch, mid + 1, r, ll, rr, v);
sum[o] = sum[lch] + sum[rch];
}
inline LL query(int o, int l, int r, int ll, int rr) {
if(l >= ll && r <= rr) return sum[o];
pushdown(o, l, r); LL res = 0;
if(ll <= mid) res += query(lch, l, mid, ll, rr);
if(rr > mid) res += query(rch, mid + 1, r, ll, rr);
return res;
}
int rt = 1;
int main() {
n = readint(); q = readint();
for(int i = 1; i <= n; i++) {
a[i] = readint();
}
for(int i = 1, u, v; i < n; i++) {
u = readint(); v = readint();
gra[u].push_back(v); gra[v].push_back(u);
}
dfs1(1); dfs2(1, 1); build(1, 1, n);
while(q--) {
int op, u, v, x;
op = readint();
if(op == 1) {
rt = readint();
} else if(op == 2) {
u = readint(); v = readint(); x = readint();
int lca = querylca(u, v), l1 = querylca(u, rt), l2 = querylca(v, rt);
if(rt == 1 || querylca(lca, rt) != lca) {
add(1, 1, n, dfn[lca], dfn[lca] + siz[lca] - 1, x);
} else {
lca = dep[l1] > dep[l2] ? l1 : l2;
add(1, 1, n, 1, n, x);
if(lca != rt) {
int p = rt;
while(dep[fa[top[p]]] > dep[lca]) p = fa[top[p]];
p = vn[dfn[p] - (dep[p] - dep[lca] - 1)];
add(1, 1, n, dfn[p], dfn[p] + siz[p] - 1, -x);
}
}
} else {
u = readint();
if(rt == 1 || querylca(u, rt) != u) {
printf("%lld\n", query(1, 1, n, dfn[u], dfn[u] + siz[u] - 1));
} else {
LL res = 0;
res += query(1, 1, n, 1, n);
if(u != rt) {
int p = rt;
while(dep[fa[top[p]]] > dep[u]) p = fa[top[p]];
p = vn[dfn[p] - (dep[p] - dep[u] - 1)];
res -= query(1, 1, n, dfn[p], dfn[p] + siz[p] - 1);
}
printf("%lld\n", res);
}
}
}
return 0;
}
题目地址:洛谷:【P2146】[NOI2015]软件包管理器 – 洛谷、BZOJ:Problem 4196. — [Noi2015]软件包管理器
Linux用户和OSX用户一定对软件包管理器不会陌生。通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软件包的安装所依赖的其它软件包),完成所有的配置。Debian/Ubuntu使用的apt-get,Fedora/CentOS使用的yum,以及OSX下可用的homebrew都是优秀的软件包管理器。
你决定设计你自己的软件包管理器。不可避免地,你要解决软件包之间的依赖问题。如果软件包A依赖软件包B,那么安装软件包A以前,必须先安装软件包B。同时,如果想要卸载软件包B,则必须卸载软件包A。现在你已经获得了所有的软件包之间的依赖关系。而且,由于你之前的工作,除0号软件包以外,在你的管理器当中的软件包都会依赖一个且仅一个软件包,而0号软件包不依赖任何一个软件包。依赖关系不存在环(若有m(m≥2)个软件包A1,A2,A3,⋯,Am,其中A1依赖A2,A2依赖A3,A3依赖A4,……,A[m-1]依赖Am,而Am依赖A1,则称这m个软件包的依赖关系构成环),当然也不会有一个软件包依赖自己。
现在你要为你的软件包管理器写一个依赖解决程序。根据反馈,用户希望在安装和卸载某个软件包时,快速地知道这个操作实际上会改变多少个软件包的安装状态(即安装操作会安装多少个未安装的软件包,或卸载操作会卸载多少个已安装的软件包),你的任务就是实现这个部分。注意,安装一个已安装的软件包,或卸载一个未安装的软件包,都不会改变任何软件包的安装状态,即在此情况下,改变安装状态的软件包数为0。
输入格式:
从文件manager.in中读入数据。
输入文件的第1行包含1个整数n,表示软件包的总数。软件包从0开始编号。
随后一行包含n−1个整数,相邻整数之间用单个空格隔开,分别表示1,2,3,⋯,n−2,n−1号软件包依赖的软件包的编号。
接下来一行包含1个整数q,表示询问的总数。之后q行,每行1个询问。询问分为两种:
install x:表示安装软件包x
uninstall x:表示卸载软件包x
你需要维护每个软件包的安装状态,一开始所有的软件包都处于未安装状态。
对于每个操作,你需要输出这步操作会改变多少个软件包的安装状态,随后应用这个操作(即改变你维护的安装状态)。
输出格式:
输出到文件manager.out中。
输出文件包括q行。
输出文件的第i行输出1个整数,为第i步操作中改变安装状态的软件包数。
输入样例#1:
7 0 0 0 1 1 5 5 install 5 install 6 uninstall 1 install 4 uninstall 0
输出样例#1:
3 1 3 2 3
输入样例#2:
10 0 1 2 1 3 0 0 3 2 10 install 0 install 3 uninstall 2 install 7 install 5 install 9 uninstall 9 install 4 install 1 install 9
输出样例#2:
1 3 2 1 3 1 1 1 0 1
n,q<=10000
考虑安装软件包即安装从根0到该点的路径上的所有软件包,则先求和算出改变数再链改值。卸载软件包即卸载其子树中所有软件包,则先求和算出改变值再子树改值。
由于0号点不好处理,我们给点的编号加1。
// Code by KSkun, 2018/4
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF
: *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
inline int readop() {
char c;
int res = 1;
while(!isalpha(c = fgc()));
while(isalpha(c)) {
if(c == 'u') res = 2;
c = fgc();
}
return res;
}
const int MAXN = 1000005;
int n, dfn[MAXN], ptn[MAXN];
std::vector<int> gra[MAXN];
// Segment Tree
#define lch o << 1
#define rch (o << 1) | 1
#define mid ((l + r) >> 1)
int val[MAXN << 2], tag[MAXN << 2];
inline void pushdown(int o, int l, int r) {
if(tag[o] != -1) {
tag[lch] = tag[rch] = tag[o];
val[lch] = tag[o] * (mid - l + 1);
val[rch] = tag[o] * (r - mid);
tag[o] = -1;
}
}
inline void merge(int o) {
val[o] = val[lch] + val[rch];
}
inline void modify(int o, int l, int r, int ll, int rr, int v) {
if(l >= ll && r <= rr) {
val[o] = v * (r - l + 1);
tag[o] = v;
return;
}
pushdown(o, l, r);
if(ll <= mid) modify(lch, l, mid, ll, rr, v);
if(rr > mid) modify(rch, mid + 1, r, ll, rr, v);
merge(o);
}
inline int query(int o, int l, int r, int ll, int rr) {
if(l >= ll && r <= rr) {
return val[o];
}
pushdown(o, l, r);
int res = 0;
if(ll <= mid) res += query(lch, l, mid, ll, rr);
if(rr > mid) res += query(rch, mid + 1, r, ll, rr);
return res;
}
int fa[MAXN], siz[MAXN], dep[MAXN], top[MAXN], son[MAXN], clk;
inline void dfs1(int u) {
siz[u] = 1;
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa[u]) continue;
fa[v] = u;
dep[v] = dep[u] + 1;
dfs1(v);
siz[u] += siz[v];
if(siz[v] > siz[son[u]]) son[u] = v;
}
}
inline void dfs2(int u, int tp) {
dfn[u] = ++clk;
ptn[dfn[u]] = u;
top[u] = tp;
if(son[u]) {
dfs2(son[u], tp);
}
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
inline void modify(int x, int y, int z) {
int tx = top[x], ty = top[y];
while(tx != ty) {
if(dep[tx] > dep[ty]) {
std::swap(x, y);
std::swap(tx, ty);
}
modify(1, 1, n, dfn[ty], dfn[y], z);
y = fa[ty];
ty = top[y];
}
if(dep[x] > dep[y]) {
std::swap(x, y);
}
modify(1, 1, n, dfn[x], dfn[y], z);
}
inline int query(int x, int y) {
int tx = top[x], ty = top[y];
int res = 0;
while(tx != ty) {
if(dep[tx] > dep[ty]) {
std::swap(x, y);
std::swap(tx, ty);
}
res += query(1, 1, n, dfn[ty], dfn[y]);
y = fa[ty];
ty = top[y];
}
if(dep[x] > dep[y]) {
std::swap(x, y);
}
res += query(1, 1, n, dfn[x], dfn[y]);
return res;
}
inline void modify(int x, int z) {
modify(1, 1, n, dfn[x], dfn[x] + siz[x] - 1, z);
}
inline int query(int x) {
return query(1, 1, n, dfn[x], dfn[x] + siz[x] - 1);
}
int q, op, x;
int main() {
memset(tag, -1, sizeof(tag));
n = readint();
for(int i = 2; i <= n; i++) {
x = readint() + 1;
gra[x].push_back(i);
gra[i].push_back(x);
}
dfs1(1);
dfs2(1, 1);
q = readint();
while(q--) {
op = readop(); x = readint() + 1;
if(op == 1) {
printf("%d\n", dep[x] + 1 - query(1, x));
modify(1, x, 1);
} else {
printf("%d\n", query(x));
modify(x, 0);
}
}
return 0;
}
题目地址:洛谷:【SP913】QTREE2 – Query on a tree II – 洛谷、SPOJ:SPOJ.com – Problem QTREE2
SPOJ QTREE系列:
You are given a tree (an undirected acyclic connected graph) with N nodes, and edges numbered 1, 2, 3…N-1. Each edge has an integer value assigned to it, representing its length.
We will ask you to perfrom some instructions of the following form:
给一棵带边权的树,操作1.询问两点路径长2.求两点有向路径上第k点。
输入格式:
The first line of input contains an integer t, the number of test cases (t <= 25). t test cases follow.
For each test case:
There is one blank line between successive tests.
输出格式:
For each “DIST” or “KTH” operation, write one integer representing its result.
Print one blank line after each test.
输入样例#1:
1 6 1 2 1 2 4 1 2 5 2 1 3 1 3 6 2 DIST 4 6 KTH 4 6 4 DONE
输出样例#1:
5 3
求和同QTREE:[SPOJ-QTREE]Query on a tree 题解 | KSkun’s Blog。查k点可以考虑算一下LCA到两个儿子的距离,看看这个点在哪条链上,然后再换算成底端往上第几个点,沿重链上跳,利用DFS序算出来即可。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int readint() {
register int res = 0, neg = 1;
register char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
inline bool isop(char c) {
return c == 'I' || c == 'H' || c == 'O';
}
inline char readop() {
register char c;
while(!isop(c = fgc()));
return c;
}
const int MAXN = 10005;
struct Edge {
int to, w, nxt;
} gra[MAXN << 1];
int head[MAXN], tot;
int T, n, m, ut, vt, wt, kt;
char op;
int w[MAXN], fa[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], ptn[MAXN], top[MAXN], dep[MAXN], cnt;
inline void dfs1(int u) {
siz[u] = 1;
son[u] = 0;
for(register int i = head[u]; i; i = gra[i].nxt) {
register int v = gra[i].to;
if(v == fa[u]) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
w[v] = gra[i].w;
dfs1(v);
siz[u] += siz[v];
if(siz[v] > siz[son[u]]) son[u] = v;
}
}
inline void dfs2(int u, int tp) {
top[u] = tp;
dfn[u] = ++cnt;
ptn[dfn[u]] = u;
if(son[u]) dfs2(son[u], tp);
for(register int i = head[u]; i; i = gra[i].nxt) {
register int v = gra[i].to;
if(v == son[u] || v == fa[u]) continue;
dfs2(v, v);
}
}
LL sgt[MAXN << 2];
inline void build(int o, int l, int r) {
if(l == r) {
sgt[o] = w[ptn[l]];
return;
}
register int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
build(lch, l, mid);
build(rch, mid + 1, r);
sgt[o] = sgt[lch] + sgt[rch];
}
inline void modify(int o, int l, int r, int x, int v) {
if(l == r) {
sgt[o] = v;
return;
}
register int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(x <= mid) modify(lch, l, mid, x, v);
else modify(rch, mid + 1, r, x, v);
sgt[o] = sgt[lch] + sgt[rch];
}
inline LL query(int o, int l, int r, int ll, int rr) {
if(l >= ll && r <= rr) {
return sgt[o];
}
register int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
register LL res = 0;
if(ll <= mid) res += query(lch, l, mid, ll, rr);
if(rr > mid) res += query(rch, mid + 1, r, ll, rr);
return res;
}
inline LL querysum(int u, int v) {
int tu = top[u], tv = top[v];
register LL res = 0;
while(tu != tv) {
if(dep[tu] > dep[tv]) {
std::swap(u, v);
std::swap(tu, tv);
}
res += query(1, 1, n, dfn[tv], dfn[v]);
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) std::swap(u, v);
if(u != v) res += query(1, 1, n, dfn[u] + 1, dfn[v]);
return res;
}
inline int querylca(int u, int v) {
int tu = top[u], tv = top[v];
while(tu != tv) {
if(dep[tu] > dep[tv]) {
std::swap(u, v);
std::swap(tu, tv);
}
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) std::swap(u, v);
return u;
}
inline int querykth(int u, int v, int k) {
int lca = querylca(u, v), tu = top[u], tv = top[v];
if(dep[u] - dep[lca] + 1 >= k) {
while(dep[tu] > dep[lca]) {
if(dep[u] - dep[tu] + 1 >= k) break;
k -= dep[u] - dep[tu] + 1;
u = fa[tu];
tu = top[u];
}
return ptn[dfn[u] - k + 1];
} else {
k -= dep[u] - dep[lca] + 1;
k = dep[v] - dep[lca] - k + 1;
while(dep[tv] > dep[lca]) {
if(dep[v] - dep[tv] + 1 >= k) break;
k -= dep[v] - dep[tv] + 1;
v = fa[tv];
tv = top[v];
}
return ptn[dfn[v] - k + 1];
}
}
inline void addedge(int u, int v, int w) {
gra[++tot] = Edge {v, w, head[u]};
head[u] = tot;
}
int main() {
T = readint();
while(T--) {
tot = cnt = 0;
memset(head, 0, sizeof(head));
n = readint();
for(int i = 1; i < n; i++) {
ut = readint(); vt = readint(); wt = readint();
addedge(ut, vt, wt);
addedge(vt, ut, wt);
}
dfs1(1);
dfs2(1, 1);
build(1, 1, n);
for(;;) {
op = readop();
if(op == 'O') break;
ut = readint();
vt = readint();
if(op == 'I') {
printf("%lld\n", querysum(ut, vt));
} else {
kt = readint();
printf("%d\n", querykth(ut, vt, kt));
}
}
printf("\n");
}
return 0;
}