题意:链上带修第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 网络管理的更多相关文章

  1. 洛谷 P4175: bzoj 1146: [CTSC2008]网络管理

    令人抓狂的整体二分题.根本原因还是我太菜了. 在学校写了一个下午写得头晕,回家里重写了一遍,一个小时就写完了--不过还是太慢. 题目传送门:洛谷P4175. 题意简述: 一棵 \(n\) 个结点的树, ...

  2. 洛谷 P4175 [CTSC2008]网络管理 解题报告

    P4175 [CTSC2008]网络管理 题目描述 带修改树上链的第\(k\)大 输入输出格式 输入格式: 第一行为两个整数\(N\)和\(Q\),分别表示路由器总数和询问的总数. 第二行有\(N\) ...

  3. 洛谷P4175 - [CTSC2008]网络管理

    Portal Description 给出一棵\(n(n\leq8\times10^4)\)个点的带点权的树,进行\(m(m\leq8\times10^4)\)次操作,操作有两种: 修改一个点的点权. ...

  4. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

  5. 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.

    没有上司的舞会  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond       题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...

  6. 洛谷P1108 低价购买[DP | LIS方案数]

    题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...

  7. 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP

    题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...

  8. 洛谷P1710 地铁涨价

    P1710 地铁涨价 51通过 339提交 题目提供者洛谷OnlineJudge 标签O2优化云端评测2 难度提高+/省选- 提交  讨论  题解 最新讨论 求教:为什么只有40分 数组大小一定要开够 ...

  9. 洛谷P1371 NOI元丹

    P1371 NOI元丹 71通过 394提交 题目提供者洛谷OnlineJudge 标签云端评测 难度普及/提高- 提交  讨论  题解 最新讨论 我觉得不需要讨论O long long 不够 没有取 ...

随机推荐

  1. mybatis源码分析(四)---------------代理对象的生成

    在mybatis两种开发方式这边文章中,我们提到了Mapper动态代理开发这种方式,现在抛出一个问题:通过sqlSession.getMapper(XXXMapper.class)来获取代理对象的过程 ...

  2. Bootstrap 字体图标(Glyphicons)

    http://www.runoob.com/bootstrap/bootstrap-glyphicons.html 什么是字体图标? 字体图标是在 Web 项目中使用的图标字体.虽然,Glyphico ...

  3. PhpStorm本地断点调试

    一.断点调试php环境搭建 1.检测本地php环境是否安装了Xdebug 在本地输出phpinfo():搜索Xdebug;如下图  如果没有安装,安装操作Xdebug如下: 将phpinfo();的信 ...

  4. python django 的环境搭建(centos)

    一.安装好nginx 二.安装uwsgi yum install python-devel -y pip3 install uwsgi #测试启动django /usr/local/python3/b ...

  5. 集合之HashSet(含JDK1.8源码分析)

    一.前言 我们已经分析了List接口下的ArrayList和LinkedList,以及Map接口下的HashMap.LinkedHashMap.TreeMap,接下来看的是Set接口下HashSet和 ...

  6. python数学第七天【期望的性质】

  7. mybatis 批量查询参数语句

    在mybatis  传入数组在sql语句中进行查询 1.传入一个map集合,已或者的形式拼接数组循环 <select id="selectUserByList" parame ...

  8. MIUI(ADUI)关闭广告推送步骤方法

    MIUI自从到了版本MIUI8之后,系统增加了各种推送,让人们所诟病.很多消费者因为这个原因,不再考虑小米手机,尽管小米手机确实很便宜. 下面就说一下如何关闭所有的MIUI 8的广告推送.方法源自MI ...

  9. mysql分页查询按某类型置顶 按某类型置尾 再按优先级排序

    近段时间接到一个新需求: 第一优先级:未满的标的顺位高于已满标的顺位.第二优先级:新手标的顺位高于其他标的的顺位. 第三优先级:标的剩余可投金额少的顺位高于标的剩余可投金额多的. 我是直接通过sql语 ...

  10. hdu-4300(kmp或者拓展kmp)

    题意:乱七八糟说了一大堆,就是先给你一个长度26的字符串,对应了abcd....xyz,这是一个密码表.然后给你一个字符串,这个字符串是不完整的(完整的应该是前半部分是加密的,后半部分是解密了的),然 ...