BZOJ 3123

丑陋的常数,BZOJ 19968ms 卡过。

感觉几个思想都挺经典但是挺套路的。

先考虑不连边的情况怎么做,区间第$k$小,想到主席树,每一个结点维护它到根的前缀和,这样子每一次查询$x$到$y$链上的点就相当于主席树上$sum(x) + sum(y) - sum(lca(x, y)) - sum(fa(lca(x, y)))$,时间复杂度一个$log$。

然后想一想连边怎么做,因为我们要维护一个森林的形态,如果暴力连边的话,相当于把$y$作为根建树连到$x$上,我们有一个优化暴力的办法就是每一次都把$siz$小的那一个点建树连到另一个点。

然后发现这样的复杂度就已经对了,证明方法和树剖类似,相当于每一次合并$siz$都会至少扩大一倍,所以对于一个大小为$n$的集合,至多会合并$logn$次,总复杂度$O(nlogn)$,这东西有一个名字叫做启发式合并。

时间复杂度$O(nlog^{2}n)$。

还有一个算是坑点的东西:

(我也是)

虽然感觉题目说得很清楚了,但是我还是没有看

Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; const int N = ;
const int Lg = ;
const int M = 4e7 + ;
const int inf = << ; int testCase, n, m, qn, tot, head[N];
int ans, maxn, ufs[N], siz[N];
int fa[N][Lg], dep[N], a[N], v[N];
bool vis[N]; struct Edge {
int to, nxt;
} e[N << ]; inline void add(int from, int to) {
e[++tot].to = to;
e[tot].nxt = head[from];
head[from] = tot;
} struct Innum {
int val, id;
} in[N]; bool cmp(const Innum &x, const Innum &y) {
if(x.val != y.val) return x.val < y.val;
else return x.id < y.id;
} inline void chkMax(int &x, int y) {
if(y > x) x = y;
} inline void discrete() {
sort(in + , in + + n, cmp);
in[].val = -inf; for(int cnt = , i = ; i <= n; i++) {
if(in[i].val != in[i - ].val) chkMax(maxn, ++cnt);
a[in[i].id] = cnt;
v[cnt] = in[i].val;
}
} inline void read(int &X) {
X = ; char ch = ; int op = ;
for(; ch > '' || ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} inline void init() {
for(int i = ; i <= n; i++)
ufs[i] = i, siz[i] = ;
} int find(int x) {
return ufs[x] == x ? x : ufs[x] = find(ufs[x]);
} namespace PSegT {
struct Node {
int lc, rc, sum;
} s[M];
int root[N], nodeCnt; #define mid ((l + r) >> 1) inline void up(int p) {
if(!p) return;
s[p].sum = s[s[p].lc].sum + s[s[p].rc].sum;
} void ins(int &p, int l, int r, int x, int pre) {
s[p = ++nodeCnt] = s[pre], s[p].sum++;
if(l == r) return; if(x <= mid) ins(s[p].lc, l, mid, x, s[pre].lc);
else ins(s[p].rc, mid + , r, x, s[pre].rc); up(p);
} int query(int x, int y, int z, int w, int l, int r, int k) {
if(l == r) return l; int now = s[s[x].lc].sum + s[s[y].lc].sum - s[s[z].lc].sum - s[s[w].lc].sum; if(k <= now) return query(s[x].lc, s[y].lc, s[z].lc, s[w].lc, l, mid, k);
else return query(s[x].rc, s[y].rc, s[z].rc, s[w].rc, mid + , r, k - now);
} } using namespace PSegT; void dfs1(int x, int fat) {
vis[x] = , dep[x] = dep[fat] + , fa[x][] = fat;
for(int i = ; i <= ; i++)
fa[x][i] = fa[fa[x][i - ]][i - ];
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
dfs1(y, x);
}
} inline void swap(int &x, int &y) {
int t = x; x = y; y = t;
} inline int getLca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = ; i >= ; i--)
if(dep[fa[x][i]] >= dep[y])
x = fa[x][i];
if(x == y) return x;
for(int i = ; i >= ; i--)
if(fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
return fa[x][];
} void dfs2(int x) {
root[x] = , ins(root[x], , maxn, a[x], root[fa[x][]]);
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fa[x][]) continue;
dfs2(y);
}
} inline void solve() {
int x, y, k;
read(x), read(y), read(k);
x ^= ans, y ^= ans, k ^= ans;
int z = getLca(x, y), w = fa[z][];
int res = query(root[x], root[y], root[z], root[w], , maxn, k);
printf("%d\n", ans = v[res]);
} int main() {
read(testCase);
for(testCase = ; testCase--; ) {
read(n), read(m), read(qn); memset(head, , sizeof(head)); tot = maxn = ;
init(); for(int i = ; i <= n; i++) {
read(a[i]);
in[i] = (Innum) {a[i], i};
}
discrete(); for(int x, y, i = ; i <= m; i++) {
read(x), read(y);
add(x, y), add(y, x); int fx = find(x), fy = find(y);
if(fx != fy) {
ufs[fx] = fy;
siz[fy] += siz[fx];
}
} memset(vis, , sizeof(vis));
memset(root, , sizeof(root)); nodeCnt = ;
for(int i = ; i <= n; i++) {
if(vis[i]) continue;
dfs1(find(i), ), dfs2(find(i));
} ans = ;
for(char op[]; qn--; ) {
scanf("%s", op);
if(op[] == 'Q') solve();
else {
int x, y;
read(x), read(y);
x ^= ans, y ^= ans; int fx = find(x), fy = find(y);
if(siz[fx] > siz[fy]) swap(fx, fy); add(x, y), add(y, x);
dfs1(y, x), dfs2(y); ufs[fy] = fx, siz[fx] += siz[fy];
}
} }
return ;
}

Luogu 3302 [SDOI2013]森林的更多相关文章

  1. BZOJ 3123: [Sdoi2013]森林 [主席树启发式合并]

    3123: [Sdoi2013]森林 题意:一个森林,加边,询问路径上k小值.保证任意时刻是森林 LCT没法搞,树上kth肯定要用树上主席树 加边?启发式合并就好了,小的树dfs重建一下 注意 测试点 ...

  2. luoguP3302 [SDOI2013]森林 主席树 启发式合并

    题目链接 luoguP3302 [SDOI2013]森林 题解 本来这题树上主席树暴力启发式合并就完了 结果把lca写错了... 以后再也不这么写了 复杂度\(O(nlog^2n)\) "f ...

  3. P3302 [SDOI2013]森林(主席树+启发式合并)

    P3302 [SDOI2013]森林 主席树+启发式合并 (我以前的主席树板子是错的.......坑了我老久TAT) 第k小问题显然是主席树. 我们对每个点维护一棵包含其子树所有节点的主席树 询问(x ...

  4. [BZOJ3123][Sdoi2013]森林 主席树+启发式合并

    3123: [Sdoi2013]森林 Time Limit: 20 Sec  Memory Limit: 512 MB Description Input 第一行包含一个正整数testcase,表示当 ...

  5. BZOJ3123: [Sdoi2013]森林(启发式合并&主席树)

    3123: [Sdoi2013]森林 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4813  Solved: 1420[Submit][Status ...

  6. 【BZOJ3123】[Sdoi2013]森林 主席树+倍增LCA+启发式合并

    [BZOJ3123][Sdoi2013]森林 Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整 ...

  7. luogu P3305 [SDOI2013]费用流

    题目链接 bz似乎挂了... luogu P3305 [SDOI2013]费用流 题解 dalao告诉我,这题 似乎很水.... 懂了题目大意就可以随便切了 问1,最大流 问2,二分最大边权求,che ...

  8. 洛谷 P3302 [SDOI2013]森林 解题报告

    P3302 [SDOI2013]森林 题目描述 小\(Z\)有一片森林,含有\(N\)个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有\(M\)条边. 小Z希望执行\(T\)个操作,操 ...

  9. 3123: [Sdoi2013]森林

    3123: [Sdoi2013]森林 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 3336  Solved: 978[Submit][Status] ...

随机推荐

  1. php程序员应该掌握的技能包

    作为一名web开发者来说,不论是php还是java web,就我目前掌握的知识来说,个人认为应该掌握以下几个方面的内容 1 基础的编程语言,这个好像是废话 2 软件设计的思想,如面向对象.mvc.各种 ...

  2. nyoj-211-Cow Contest(floyd算法)

    题目链接 /* Name:nyoj-211-Cow Contest Copyright: Author: Date: 2018/4/27 21:02:06 Description: floyd算法 大 ...

  3. 统计日志中ip出现的次数

    grep -r 'GET /weixin/weixin_izp/index.html' ./chunyun.access.log > ~/access.log cat access.log |a ...

  4. HDU - 5977 Garden of Eden (树形dp+容斥)

    题意:一棵树上有n(n<=50000)个结点,结点有k(k<=10)种颜色,问树上总共有多少条包含所有颜色的路径. 我最初的想法是树形状压dp,设dp[u][S]为以结点u为根的包含颜色集 ...

  5. 19年博客flag

    目录 为什么没有年终总结 为什么今天更新了 19年博客flag 个人博客链接:我在马路边 https://hhongwen.cn 更好的阅读体验点击查看原文:19年博客flag 原创博客,转载请注明出 ...

  6. Phone numbers

    Phone number in Berland is a sequence of n digits. Often, to make it easier to memorize the number, ...

  7. vector的简单运用(士兵队列训练问题)

    某部队进行新兵队列训练,将新兵从一开始按顺序依次编号,并排成一行横队,训练的规则如下:从头开始一至二报数,凡报到二的出列,剩下的向小序号方向靠拢,再从头开始进行一至三报数,凡报到三的出列,剩下的向小序 ...

  8. PHP开发中的问题

    1.echo不输出值,有可能是该值不存在. 2.PHP+MySQL上一条下一条记录查询 id作为自增int SQL语句如下即可:上一条 SELECT * FROM A WHERE id < $i ...

  9. 学习动态性能表(7)--v$process

    学习动态性能表 第七篇--V$PROCESS  2007.5.30 本视图包含当前系统oracle运行的所有进程信息.常被用于将oracle或服务进程的操作系统进程ID与数据库session之间建立联 ...

  10. untra edit 自动补全

    选择“高级-配置-编辑器-自动完成”,勾选“自动显示‘自动完成’对话框”,并在其中定义好“当输入x个字符”时,自动补全.