题意:链上带修第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测试用例

    package cn.zhangxueliang.mybatis.mapper; import static org.junit.Assert.*; import java.io.InputStrea ...

  2. 获取打开页面时的当前时间(yyyy-MM-dd hh:mm:ss)

    Date.prototype.Format = function (fmt) { var o = { "M+": this.getMonth() + 1, //月份 "d ...

  3. 将WCF寄宿在托管的Windows服务中

    在我之前的一篇博客中我介绍了如何发布WCF服务并将该服务寄宿于IIS上,今天我再来介绍一种方式,就是将WCF服务寄宿在Windows服务中,这样做有什么好处呢?当然可以省去部署IIS等一系列的问题,能 ...

  4. vue 关于生命周期

    序言: 1. vue 单组件的生命周期: 2. vue 父子组件的生命周期: 3. axios 异步请求 与 vue 的组件周期: 一.vue 每个组件的生命周期 关于每个组件的生命周期,官方文档里也 ...

  5. Nginx 防盗链 secure_link 模块

    L:76 需要通过 --with-http_secure_link_module 编译进Nginx secure_link 指令 Syntax: secure_link expression; Def ...

  6. Nginx http2.0

    109/110 HTTP2.0协议 优势必须使用TLS加密 传输数据量大幅减少 1:以二进制格式传输  2:标头压缩(header做压缩) 多路复用及相关功能 : 消息优先级 (比如样式表先渲染页面那 ...

  7. Auth模块使用方法大全

    auth认证 导包 from django.contrib import auth 默认数据库中使用auth_user表 创建超级用户 python manage.py createsuperuser ...

  8. CentOS 安装、配置supervisord

    负责在启动自身时启动管理的子进程,响应客户端的命令,重启崩溃或退出的子进程,记录子进程stdout和stderr输出,生成和处理子进程生命周期中的事件. 安装yum install superviso ...

  9. 如何使用JPQL写纯SQL语句

    使用JPQL,需要把SQL语句修改成类似HQL 语句.SQL 查询的是数据库,而JPQL 查询的是对象和属性,在语法上是有些不同的.对于有些用JPQL 无法写出来的查询,还是使用原生SQL写出来方便 ...

  10. Velocity 快捷键

    快捷键(2018-03-08) General Keyboard Shortcuts The following shortcuts are available anywhere within the ...