[BZOJ1468]Tree 题解
题目地址:ė …
May all the beauty be blessed.
题目地址:BZOJ:Problem 3589. — 动态树
小明在楼下种了一棵动态树, 该树每天会在某些节点上长出一些果子. 这棵树的根节点为1, 它有n个节点, n-1条边.
别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件
事件0:
这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子.
事件1:
小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次.
初始时, 每个节点上都没有果子.
输入格式:
第一行一个整数n(1<=n<=200,000), 即节点数.
接下来n-1行, 每行两个数字u, v. 表示果子u和果子v之间有一条直接的边. 节点从1开始编号.
在接下来一个整数nQ(1<=nQ<=200,000), 表示事件.
最后nQ行, 每行开头要么是0, 要么是1.
如果是0, 表示这个事件是事件0. 这行接下来的2个整数u, delta表示以u为根的子树中的每个节点长出了delta个果子.
如果是1, 表示这个事件是事件1. 这行接下来一个整数K(1<=K<=5), 表示这次询问涉及K个树枝. 接下来K对整数u_k, v_k, 每个树枝从节点u_k到节点v_k. 由于果子数可能非常多, 请输出这个数模2^31的结果.
输出格式:
对于每个事件1, 输出询问的果子数.
输入样例#1:
5 1 2 2 3 2 4 1 5 3 0 1 1 0 2 3 1 2 3 1 1 4
输出样例#1:
13
1 <= n <= 200,000, 1 <= nQ <= 200,000, K = 5.
生成每个树枝的过程是这样的:先在树中随机找一个节点, 然后在这个节点到根的路径上随机选一个节点, 这两个节点就作为树枝的两端.
又是树链信息,套树剖。考虑线段树维护区间和,然后使用打标记的形式来处理链并的问题。链上的点都打上标记,只有打标记的点的权值才会计入答案,这个线段树写是容易的。记得每次清空这次打过的标记。
至于那个取模,模数是2147483648,我们可以用int自然溢出以后对2147483647取与,这样就能把符号位搞掉,得到取模后的答案,减小常数。
吐槽:不要用memset处理很大的数组,会TLE。线段树做是O(\log n)比memsetO(n)优。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>
#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;
}
const int MAXN = 200005;
int n, q, ut, vt, op, xt, kt;
int fa[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], ptn[MAXN], top[MAXN], dep[MAXN], cnt;
std::vector<int> gra[MAXN];
inline void addedge(int u, int v) {
gra[u].push_back(v);
gra[v].push_back(u);
}
inline void dfs1(int u) {
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa[u]) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
dfs1(v);
siz[u] += siz[v] + 1;
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(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == son[u] || v == fa[u]) continue;
dfs2(v, v);
}
}
int val[MAXN << 2], sum[MAXN << 2], add[MAXN << 2], tag[MAXN << 2];
inline void pushdown(int o, int l, int r) {
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(add[o]) {
add[lch] += add[o];
add[rch] += add[o];
val[lch] += add[o] * (mid - l + 1);
val[rch] += add[o] * (r - mid);
add[o] = 0;
}
if(tag[o] != -1) {
tag[lch] = tag[rch] = tag[o];
sum[lch] = val[lch] * tag[o];
sum[rch] = val[rch] * tag[o];
tag[o] = -1;
}
}
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);
add[o] += v;
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(ll <= mid) modify(lch, l, mid, ll, rr, v);
if(rr > mid) modify(rch, mid + 1, r, ll, rr, v);
val[o] = val[lch] + val[rch];
}
inline void select(int o, int l, int r, int ll, int rr, int v) {
if(l >= ll && r <= rr) {
sum[o] = val[o] * v;
tag[o] = v;
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(ll <= mid) select(lch, l, mid, ll, rr, v);
if(rr > mid) select(rch, mid + 1, r, ll, rr, v);
sum[o] = sum[lch] + sum[rch];
}
inline void modify(int u, int v, int x) {
int tu = top[u], tv = top[v];
while(tu != tv) {
if(dep[tu] > dep[tv]) {
std::swap(u, v);
std::swap(tu, tv);
}
modify(1, 1, n, dfn[tv], dfn[v], x);
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) std::swap(u, v);
modify(1, 1, n, dfn[u], dfn[v], x);
}
inline void select(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);
}
select(1, 1, n, dfn[tv], dfn[v], 1);
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) std::swap(u, v);
select(1, 1, n, dfn[u], dfn[v], 1);
}
int main() {
n = readint();
for(int i = 1; i < n; i++) {
ut = readint(); vt = readint();
addedge(ut, vt);
}
dfs1(1);
dfs2(1, 1);
q = readint();
while(q--) {
op = readint();
if(op == 0) {
ut = readint(); xt = readint();
modify(1, 1, n, dfn[ut], dfn[ut] + siz[ut], xt);
} else {
kt = readint();
while(kt--) {
ut = readint(); vt = readint();
select(ut, vt);
}
printf("%d\n", sum[1] & 2147483647);
select(1, 1, n, 1, n, 0);
}
}
return 0;
}
题目地址:洛谷:【P2486】[SDOI2011]染色 – 洛谷、BZOJ:Problem 2243. — [SDOI2011]染色
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),
如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。
输入格式:
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面 行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。
输出格式:
对于每个询问操作,输出一行答案。
输入样例#1:
6 5 2 2 1 2 1 1 1 2 1 3 2 4 2 5 2 6 Q 3 5 C 2 1 1 Q 3 5 C 5 1 2 Q 3 5
输出样例#1:
3 1 2
数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。
树链信息自然就是树剖。线段树维护当前区间左右端颜色以及这段区间中间有几段。合并检查拼接点是否同样颜色。树剖上跳的过程类似,实现见代码。
// Code by KSkun, 2018/3
#include <cstdio>
#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 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 bool isop(char c) {
return c == 'Q' || c == 'C';
}
inline char readop() {
char c;
while(!isop(c = fgc()));
return c;
}
const int MAXN = 100005;
int n, m, w[MAXN], ut, vt, at, bt, ct;
char op;
int fa[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], ptn[MAXN], top[MAXN], dep[MAXN], cnt;
std::vector<int> gra[MAXN];
inline void addedge(int u, int v) {
gra[u].push_back(v);
gra[v].push_back(u);
}
inline void dfs1(int u) {
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa[u]) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
dfs1(v);
siz[u] += siz[v] + 1;
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(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == son[u] || v == fa[u]) continue;
dfs2(v, v);
}
}
struct Node {
int l, r, tot;
bool tag;
} sgt[MAXN << 2];
inline void pushdown(int o, int l, int r) {
int lch = o << 1, rch = (o << 1) | 1;
if(sgt[o].tag) {
sgt[lch].l = sgt[lch].r = sgt[rch].l = sgt[rch].r = sgt[o].l;
sgt[lch].tag = sgt[rch].tag = true;
sgt[lch].tot = sgt[rch].tot = 1;
sgt[o].tag = false;
}
}
inline void merge(Node &rt, Node ls, Node rs) {
rt.l = ls.l;
rt.r = rs.r;
rt.tot = ls.tot + rs.tot;
if(ls.r == rs.l) rt.tot--;
}
inline void build(int o, int l, int r) {
if(l == r) {
sgt[o].l = sgt[o].r = w[ptn[l]];
sgt[o].tot = 1;
return;
}
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
build(lch, l, mid);
build(rch, mid + 1, r);
merge(sgt[o], sgt[lch], sgt[rch]);
}
inline void modify(int o, int l, int r, int ll, int rr, int v) {
if(l == ll && r == rr) {
sgt[o].l = sgt[o].r = v;
sgt[o].tot = 1;
sgt[o].tag = true;
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(rr <= mid) {
modify(lch, l, mid, ll, rr, v);
} else if(ll > mid) {
modify(rch, mid + 1, r, ll, rr, v);
} else {
modify(lch, l, mid, ll, mid, v);
modify(rch, mid + 1, r, mid + 1, rr, v);
}
merge(sgt[o], sgt[lch], sgt[rch]);
}
inline Node query(int o, int l, int r, int ll, int rr) {
if(l == ll && r == rr) {
return sgt[o];
}
pushdown(o, l, r);
int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
if(rr <= mid) {
return query(lch, l, mid, ll, rr);
} else if(ll > mid) {
return query(rch, mid + 1, r, ll, rr);
} else {
Node ls = query(lch, l, mid, ll, mid), rs = query(rch, mid + 1, r, mid + 1, rr), res;
merge(res, ls, rs);
return res;
}
}
inline void modify(int u, int v, int c) {
int tu = top[u], tv = top[v];
while(tu != tv) {
if(dep[tu] > dep[tv]) {
std::swap(tu, tv);
std::swap(u, v);
}
modify(1, 1, n, dfn[tv], dfn[v], c);
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) std::swap(u, v);
modify(1, 1, n, dfn[u], dfn[v], c);
}
inline int query(int u, int v) {
Node t;
int res = 0, cu = -1, cv = -1, tu = top[u], tv = top[v];
while(tu != tv) {
if(dep[tu] > dep[tv]) {
std::swap(tu, tv);
std::swap(u, v);
std::swap(cu, cv);
}
t = query(1, 1, n, dfn[tv], dfn[v]);
res += t.tot;
if(t.r == cv) res--;
cv = t.l;
v = fa[tv];
tv = top[v];
}
if(dep[u] > dep[v]) {
std::swap(u, v);
std::swap(cu, cv);
}
t = query(1, 1, n, dfn[u], dfn[v]);
res += t.tot;
if(t.l == cu) res--;
if(t.r == cv) res--;
return res;
}
int main() {
n = readint(); m = readint();
for(int i = 1; i <= n; i++) w[i] = readint();
for(int i = 1; i < n; i++) {
ut = readint(); vt = readint();
addedge(ut, vt);
}
dfs1(1);
dfs2(1, 1);
build(1, 1, n);
while(m--) {
op = readop();
at = readint();
bt = readint();
if(op == 'Q') {
printf("%d\n", query(at, bt));
}
if(op == 'C') {
ct = readint();
modify(at, bt, ct);
}
}
return 0;
}
题目地址:HDUOJ:Problem – 5385
You have a connected directed graph.Let d(x) be the length of the shortest path from 1 to x.Specially d(1)=0.A graph is good if there exist x satisfy d(1)<d(2)<….d(x)>d(x+1)>…d(n).Now you need to set the length of every edge satisfy that the graph is good.Specially,if d(1)<d(2)<..d(n),the graph is good too.
The length of one edge must ∈ [1,n]
It’s guaranteed that there exists solution.
给你一个有向图,你要确定每条边的边权,使得存在1到每个点的最短路距离d满足d(1)<d(2)<….d(x)>d(x+1)>…d(n)。
输入格式:
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
The first line contains two integers n and m,the number of vertexs and the number of edges.Next m lines contain two integers each, ui and vi (1≤ui,vi≤n), indicating there is a link between nodes ui and vi and the direction is from ui to vi.
∑n≤3∗10^5,∑m≤6∗10^5
1≤n,m≤10^5
输出格式:
For each test case,print m lines.The i-th line includes one integer:the length of edge from ui to vi
输入样例#1:
2 4 6 1 2 2 4 1 3 1 2 2 2 2 3 4 6 1 2 2 3 1 4 2 1 2 1 2 1
输出样例#1:
1 2 2 1 4 4 1 1 3 4 4 4
其实这道题如果能想到Prim算法的流程就好做了。Prim是确定一个点的d为0,然后向外扩展到当前选定点集距离最小的点,扩展完毕以后就是一棵最短路树/最小生成树。那么我们也来构造这样一棵最短路树/最小生成树,使得树边满足题目要求且非树边边权极大就可以了。
我们确定1为树根,d(1)=0,然后从最小号点和最大号点开始往里加点,每加一个点就给它的出边和相邻点打标记,表示这些边/点以后应当计算。
虽然数据范围长得很像O(n \log n),但其实算法是O(m)的。
// 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 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;
}
const int MAXN = 600005;
struct Edge {
int u, v, id;
};
int T, n, m, ut, vt, dis[MAXN];
std::vector<Edge> gra[MAXN], edges;
bool vis[MAXN], ans[MAXN];
inline void work(int u, int d) {
dis[u] = d;
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i].v;
if(vis[v]) continue;
vis[v] = true;
ans[gra[u][i].id] = true;
}
}
int main() {
T = readint();
while(T--) {
n = readint(); m = readint();
memset(dis, 0, sizeof(dis));
memset(ans, 0, sizeof(ans));
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++) gra[i].clear();
edges.clear();
for(int i = 1; i <= m; i++) {
ut = readint(); vt = readint();
gra[ut].push_back(Edge {ut, vt, i});
edges.push_back(Edge {ut, vt, i});
}
int l = 1, r = n, d = 0;
vis[l] = true;
while(l <= r) {
if(vis[l]) work(l++, d++);
if(vis[r]) work(r--, d++);
}
for(int i = 1; i <= m; i++) {
if(ans[i]) {
printf("%d\n", std::abs(dis[edges[i - 1].u] - dis[edges[i - 1].v]));
} else {
printf("%d\n", n);
}
}
}
return 0;
}