洛谷P4175 网络管理
题意:链上带修第k大。
这毒瘤题。。。别看题意只有7个字,能把我吊打死。。。
介绍其中两种做法好了。其实思想上是一样的。
对于每一个点,建立权值线段树,维护它到根路径上的所有权值。
一条路径上的点集就是x + y - z - fa z,此处z是lca x y
这样查询就可以轻易做到了。怎么建出来呢?
考虑每个点都要在它的子树中插入。那么我们搞出DFS序来,子树就是上面的一段区间。
我们就要对于这个DFS序,支持区间加(插入),单点求值。很容易想到树状数组+差分。
那么我们就用树状数组维护差分后的值域线段树,这样就是单点修改和区间求和了。
也就是树状数组套线段树...
具体实现上,查询操作有点小技巧。因为要同时查4个位置然后加加减减,每个位置又会涉及查询log棵线段树,所以就用一个now i来维护i版本的线段树当前走到的节点,用一个栈来存要用到的线段树以便更新。
然后修改操作嘛,就是删除之前的再加上新的。
时间复杂度nlog2n。
#include <cstdio>
#include <algorithm> const int N = , lm = 1e8, M = ; struct Edge {
int nex, v;
}edge[N << ]; int top; int e[N], p[N], num, val[N], pos[N], n, ed[N], fa[N][], tot, pw[N], d[N], rt[N], now[N], tp;
int sum[M], ls[M], rs[M];
bool vis[N]; inline void add(int x, int y) {
top++;
edge[top].v = y;
edge[top].nex = e[x];
e[x] = top;
return;
} void DFS(int x, int f) {
fa[x][] = f;
d[x] = d[f] + ;
pos[x] = ++num;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y != f) {
DFS(y, x);
}
}
ed[x] = num;
return;
} void Add(int p, int v, int l, int r, int &o) {
if(!o) {
o = ++tot;
}
if(l == r) {
sum[o] += v;
return;
}
int mid = (l + r) >> ;
if(p <= mid) {
Add(p, v, l, mid, ls[o]);
}
else {
Add(p, v, mid + , r, rs[o]);
}
sum[o] = sum[ls[o]] + sum[rs[o]];
return;
} inline void insert(int id, int p, int v) {
for(int i = id; i <= num; i += (i & (-i))) {
Add(p, v, , lm, rt[i]);
}
return;
} inline int lca(int x, int y) {
if(d[x] > d[y]) {
std::swap(x, y);
}
int t = pw[n];
while(t >= && d[y] > d[x]) {
if(d[fa[y][t]] >= d[x]) {
y = fa[y][t];
}
t--;
}
if(x == y) {
return x;
}
t = pw[n];
while(t >= && fa[x][] != fa[y][]) {
if(fa[x][t] != fa[y][t]) {
x = fa[x][t];
y = fa[y][t];
}
t--;
}
return fa[x][];
} inline int ask(int k, int x, int y) {
int z = lca(x, y);
if(d[x] + d[y] - * d[z] + < k) {
return -;
}
// x + y - z - fa[z]
int w = fa[z][];
x = pos[x], y = pos[y], z = pos[z], w = pos[w];
for(int i = x; i >= ; i -= (i & (-i))) {
if(!vis[i]) {
vis[i] = ;
p[++tp] = i;
now[i] = rt[i];
}
}
for(int i = y; i >= ; i -= (i & (-i))) {
if(!vis[i]) {
vis[i] = ;
p[++tp] = i;
now[i] = rt[i];
}
}
for(int i = z; i >= ; i -= (i & (-i))) {
if(!vis[i]) {
vis[i] = ;
p[++tp] = i;
now[i] = rt[i];
}
}
for(int i = w; i >= ; i -= (i & (-i))) {
if(!vis[i]) {
vis[i] = ;
p[++tp] = i;
now[i] = rt[i];
}
}
//
int l = , r = lm;
while(l < r) {
int mid = (l + r) >> , s = ;
for(int i = x; i >= ; i -= i & (-i)) {
s += sum[rs[now[i]]];
}
for(int i = y; i >= ; i -= i & (-i)) {
s += sum[rs[now[i]]];
}
for(int i = z; i >= ; i -= i & (-i)) {
s -= sum[rs[now[i]]];
}
for(int i = w; i >= ; i -= i & (-i)) {
s -= sum[rs[now[i]]];
}
if(k <= s) { // rs
for(int i = ; i <= tp; i++) {
now[p[i]] = rs[now[p[i]]];
}
l = mid + ;
}
else { // ls
k -= s;
for(int i = ; i <= tp; i++) {
now[p[i]] = ls[now[p[i]]];
}
r = mid;
}
}
for(int i = ; i <= tp; i++) {
vis[p[i]] = ;
}
tp = ;
return r;
} inline void init() {
for(int i = ; i <= n; i++) {
pw[i] = pw[i >> ] + ;
}
for(int j = ; j <= pw[n]; j++) {
for(int i = ; i <= n; i++) {
fa[i][j] = fa[fa[i][j - ]][j - ];
}
}
return;
} int main() {
int q;
scanf("%d%d", &n, &q);
for(int i = ; i <= n; i++) {
scanf("%d", &val[i]);
}
for(int i = , x, y; i < n; i++) {
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
// prework DFS(, );
init();
for(int i = ; i <= n; i++) {
// [pos[i], ed[i]] insert val[i] 1
insert(pos[i], val[i], );
insert(ed[i] + , val[i], -);
} for(int i = , f, x, y; i <= q; i++) {
scanf("%d%d%d", &f, &x, &y);
if(f == ) { // change val[x] = y
// [pos[x] ed[x]] insert val[x]
insert(pos[x], val[x], -);
insert(ed[x] + , val[x], );
insert(pos[x], y, );
insert(ed[x] + , y, -);
val[x] = y;
}
else { /// ask fth
int t = ask(f, x, y);
if(t == -) {
puts("invalid request!");
}
else {
printf("%d\n", t);
}
}
}
return ;
}
AC代码
接下来是线段树套线段树的写法。
离散化之后外层值域线段树,内层线段树每个点按照DFS序维护,该点到根的路径上,有多少点的权值在外层树这个范围内。
查询的时候也是值域线段树上二分。利用lca转化成4个内层线段树单点求值再加加减减。
建树的时候,每个点要在外层树上从上往下log个内层树中插入。内层树中要在它的子树插入,又是一段区间。
修改就是跟建树差不多的操作,删去旧的再加上新的。
开了O2还慢的飞起...好歹是过了。
时间复杂度同上。
// luogu-judger-enable-o2
#include <cstdio>
#include <algorithm> const int N = , M = ; struct Node {
int f, x, y;
}node[N]; struct Edge {
int nex, v;
}edge[N << ]; int top; int e[N], n, num, tot, pw[N], pos[N], ed[N], fa[N][], d[N], rt[N << ], temp, val[N], X[N << ];
int tag[M], ls[M], rs[M], sum[M]; inline void add(int x, int y) {
top++;
edge[top].v = y;
edge[top].nex = e[x];
e[x] = top;
return;
} inline void pushdown(int l, int r, int o) {
if(!tag[o]) {
return;
}
if(!ls[o]) {
ls[o] = ++tot;
}
if(!rs[o]) {
rs[o] = ++tot;
}
tag[ls[o]] += tag[o];
tag[rs[o]] += tag[o];
int mid = (l + r) >> ;
sum[ls[o]] += tag[o] * (mid - l + );
sum[rs[o]] += tag[o] * (r - mid);
tag[o] = ;
return;
} void DFS(int x, int f) {
fa[x][] = f;
d[x] = d[f] + ;
pos[x] = ++num;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y != f) {
DFS(y, x);
}
}
ed[x] = num;
return;
} inline void init() {
for(int i = ; i <= n; i++) {
pw[i] = pw[i >> ] + ;
}
for(int j = ; j <= pw[n]; j++) {
for(int i = ; i <= n; i++) {
fa[i][j] = fa[fa[i][j - ]][j - ];
}
}
return;
} inline int lca(int x, int y) {
if(d[x] > d[y]) {
std::swap(x, y);
}
int t = pw[n];
while(t >= && d[y] > d[x]) {
if(d[fa[y][t]] >= d[x]) {
y = fa[y][t];
}
t--;
}
if(x == y) {
return x;
}
t = pw[n];
while(t >= && fa[x][] != fa[y][]) {
if(fa[x][t] != fa[y][t]) {
x = fa[x][t];
y = fa[y][t];
}
t--;
}
return fa[x][];
} void Add(int L, int R, int v, int l, int r, int &o) {
//printf("add : %d %d %d %d %d %d \n", L, R, v, l, r, o);
if(!o) {
o = ++tot;
}
if(L <= l && r <= R) {
tag[o] += v;
sum[o] += (r - l + ) * v;
return;
}
pushdown(l, r, o);
int mid = (l + r) >> ;
if(L <= mid) {
Add(L, R, v, l, mid, ls[o]);
}
if(mid < R) {
Add(L, R, v, mid + , r, rs[o]);
}
sum[o] = sum[ls[o]] + sum[rs[o]];
return;
} void insert(int L, int R, int v, int p, int l, int r, int o) {
//printf("insert val [%d %d] \n", l, r);
Add(L, R, v, , n, rt[o]);
if(l == r) {
return;
}
int mid = (l + r) >> ;
if(p <= mid) {
insert(L, R, v, p, l, mid, o << );
}
else {
insert(L, R, v, p, mid + , r, o << | );
}
return;
} int getSum(int p, int l, int r, int o) {
if(!o) {
return ;
}
if(l == r) {
return sum[o];
}
int mid = (l + r) >> ;
pushdown(l, r, o);
if(p <= mid) {
return getSum(p, l, mid, ls[o]);
}
else {
return getSum(p, mid + , r, rs[o]);
}
} int Ask(int k, int x, int y, int z, int w, int l, int r, int o) {
if(l == r) {
return r;
}
int s = ;
s += getSum(x, , n, rt[o << | ]);
s += getSum(y, , n, rt[o << | ]);
s -= getSum(z, , n, rt[o << | ]);
if(w) {
s -= getSum(w, , n, rt[o << | ]);
}
int mid = (l + r) >> ;
if(k <= s) {
return Ask(k, x, y, z, w, mid + , r, o << | );
}
else {
k -= s;
return Ask(k, x, y, z, w, l, mid, o << );
}
} inline int ask(int k, int x, int y) {
int z = lca(x, y);
if(d[x] + d[y] - d[z] * + < k) {
return -;
}
int w = fa[z][];
x = pos[x];
y = pos[y];
z = pos[z];
w = pos[w];
return Ask(k, x, y, z, w, , temp, );
} int main() {
int q;
scanf("%d%d", &n, &q);
for(int i = ; i <= n; i++) {
scanf("%d", &val[i]);
X[++temp] = val[i];
}
for(int i = , x, y; i < n; i++) {
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
for(int i = ; i <= q; i++) {
scanf("%d%d%d", &node[i].f, &node[i].x, &node[i].y);
if(node[i].f == ) {
X[++temp] = node[i].y;
}
}
// prework
std::sort(X + , X + temp + );
temp = std::unique(X + , X + temp + ) - X - ;
for(int i = ; i <= n; i++) {
val[i] = std::lower_bound(X + , X + temp + , val[i]) - X;
}
for(int i = ; i <= q; i++) {
if(node[i].f == ) {
node[i].y = std::lower_bound(X + , X + temp + , node[i].y) - X;
}
} DFS(, );
init();
for(int i = ; i <= n; i++) {
insert(pos[i], ed[i], , val[i], , temp, );
}
for(int i = , f, x, y; i <= q; i++) {
f = node[i].f;
x = node[i].x;
y = node[i].y;
if(f == ) { // change
insert(pos[x], ed[x], -, val[x], , temp, );
insert(pos[x], ed[x], , y, , temp, );
val[x] = y;
}
else { // ask fth
int t = ask(f, x, y);
if(t == -) {
puts("invalid request!");
}
else {
printf("%d\n", X[t]);
}
}
}
return ;
}
AC代码
写的有点乱...
洛谷P4175 网络管理的更多相关文章
- 洛谷 P4175: bzoj 1146: [CTSC2008]网络管理
令人抓狂的整体二分题.根本原因还是我太菜了. 在学校写了一个下午写得头晕,回家里重写了一遍,一个小时就写完了--不过还是太慢. 题目传送门:洛谷P4175. 题意简述: 一棵 \(n\) 个结点的树, ...
- 洛谷 P4175 [CTSC2008]网络管理 解题报告
P4175 [CTSC2008]网络管理 题目描述 带修改树上链的第\(k\)大 输入输出格式 输入格式: 第一行为两个整数\(N\)和\(Q\),分别表示路由器总数和询问的总数. 第二行有\(N\) ...
- 洛谷P4175 - [CTSC2008]网络管理
Portal Description 给出一棵\(n(n\leq8\times10^4)\)个点的带点权的树,进行\(m(m\leq8\times10^4)\)次操作,操作有两种: 修改一个点的点权. ...
- 洛谷1640 bzoj1854游戏 匈牙利就是又短又快
bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...
- 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.
没有上司的舞会 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...
- 洛谷P1108 低价购买[DP | LIS方案数]
题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...
- 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP
题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...
- 洛谷P1710 地铁涨价
P1710 地铁涨价 51通过 339提交 题目提供者洛谷OnlineJudge 标签O2优化云端评测2 难度提高+/省选- 提交 讨论 题解 最新讨论 求教:为什么只有40分 数组大小一定要开够 ...
- 洛谷P1371 NOI元丹
P1371 NOI元丹 71通过 394提交 题目提供者洛谷OnlineJudge 标签云端评测 难度普及/提高- 提交 讨论 题解 最新讨论 我觉得不需要讨论O long long 不够 没有取 ...
随机推荐
- Oracle pivot行转列函数案例
with temp as( select '湖北省' province,'武汉市' city,'第一' ranking from dual union all select '湖北省' provinc ...
- centOS 7下无法启动网络(service network start)错误解决办法
今天在centOS 7下更改完静态ip后发现network服务重启不了,翻遍了网络,尝试了各种方法,终于解决了. 现把各种解决方法归纳整理,希望能让后面的同学少走点歪路... 首先看问题:执行serv ...
- netstat -na 查看有大量TIME_WAIT解决办法(修改内核参数)
# netstat -an|awk '/tcp/ {print $6}'|sort|uniq -c 16 CLOSING 130 ESTABLISHED 298 FIN_WA ...
- js数组中两个有相同删除一个
for (var i = 0; i < project.Before.length; i++) { var j = 0; ...
- 在web-inf外面 使用的是绝对路径进行访问 “/”表示访问文件夹 一层一层方式 我们在windos下访问文件夹也是一层一层的访问
- Uncaught SyntaxError: Unexpected token export
开发过程中遇到这个错误,虽然不影响使用,但是每次浏览器控制台都会有错误输出,看起来十分不舒服,故翻阅资料发现是因为浏览器虽然支持了es6,但是不支持es6的Module直接使用,需要在script标签 ...
- 【数学建模】day08-数理统计III
2. 回归分析 回归分析与曲线拟合区分. 曲线拟合是,根据得到的若干有关变量的一组数据,寻找因变量与(一个或几个)自变量之间的一个函数,使这个函数对那组数据拟合得好.通常,函数的形式可以由经验.先验知 ...
- mysql操作命令
一.MySQL连接命令 mysql-u:指定用户-p:指定密码-S:指定socket文件-P:指定端口-h:指定主机域-e:指定sql语句 mysql> status \s 查看状态 mysql ...
- 基准对象object中的基础类型----列表 (四)
object有如下子类: CLASSES object basestring str unicode buffer bytearray classmethod complex dict enumera ...
- [NOI2002] 贪吃的九头龙
题目类型:树形DP 传送门:>Here< 题意:有一只九头龙要吃了一颗树,给出一棵\(N\)个节点的带边权的树.九头龙有\(M\)个头,其中一个是大头,大头要吃恰好\(K\)个节点,其他头 ...