BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋 (替罪羊树 动态点分治 套 Treap)
题意
分析
引用PoPoQQQ的话
吾辈有生之年终于把这道题切了。。。QAQ
(蒟蒻狂笑)
Orz PoPoQQQ,我又抄PoPoQQQ的题解了 …
- 突然发现有旋Treap没那么难写
- 学习了一波C++语法,第一次用指针,什么new/deletenew/deletenew/delete也大概会用了…(这玩意还能重载…跪了)
- 学习了一波点分治的正确写法.我之前写的都是什么烂玩意儿
- dalao代码的细节处理得好啊,学习了学习了 (这就是你抄代码的原因?)
这种符合某条件的点对,首先就想到点分治…先假设树的形态是固定的,我们考虑满足dis(i,j)<=ri+rjdis(i,j)<=r_i+r_jdis(i,j)<=ri+rj的点对,假设它们在点分树上的lcalcalca为uuu,定义did_idi为iii到uuu的距离.那么有di+dj<=ri+rjdi−ri<=rj−djd_i+d_j<=r_i+r_j\\d_i-r_i<=r_j-d_jdi+dj<=ri+rjdi−ri<=rj−dj 那么我们将子树中所有di−rid_i-r_idi−ri插入平衡树,只需要查询rj−djr_j-d_jrj−dj在平衡树中的排名就能查询和jjj组成点对的iii的数量.
由于树的形态不确定,那么就动态点分治就行了.那么我们像点分治常见的套路,在点分树上的每一个点维护两颗平衡树,一棵ttt表示子树内的所有点di−rid_i-r_idi−ri组成的平衡树,一棵tftftf维护这一棵子树对父亲的贡献.那么查询一个点的时候,从那个点往上计算,每一次用父亲的ttt所查询的值减去这棵子树的tftftf所查询的值 就能不重不漏地算出答案(因为要去除点对在同一棵子树内的情况).然后修改可以和查询在同一个函数内进行,先查后修改.
由于动态点分治可能会出现一条链的情况,那么要像替罪羊树的思想,子树不平衡就重构…
用setsetset存边挺省事的,就是比较慢…
CODE
手写rand真快 … 要更详细的代码注释去PoPoQQQ看
#include <set>
#include <queue>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &res) {
char ch; int flg = 1; for(;!isdigit(ch=getchar());)if(ch=='-')flg=-flg;
for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0'); res*=flg;
}
typedef long long LL;
const int MAXN = 100100;
const int mod = 1e9;
int n, r[MAXN];
LL lastans;
int Rand() { //手写rand(qwq)
static int G = 3;
return G = 3ll * G % 998244353;
}
namespace Gragh {
struct edge { int to, nxt, len, ban; }e[MAXN<<1];
int fir[MAXN], cnt = 1, f[MAXN][17], dep[MAXN], dis[MAXN];
inline void Add(int u, int v, int w) {
e[++cnt] = (edge) { v, fir[u], w, -1 }, fir[u] = cnt;
}
inline void Build_LCA(int i) {
for(int j = 1; j < 17; ++j)
f[i][j] = f[f[i][j-1]][j-1];
}
inline int LCA(int u, int v) {
if(dep[u] < dep[v]) swap(u, v);
for(int i = 16; ~i; --i)
if((dep[u]-dep[v])&(1<<i)) u = f[u][i];
if(u == v) return u;
for(int i = 16; ~i; --i)
if(f[u][i] != f[v][i])
u = f[u][i], v = f[v][i];
return f[u][0];
}
inline int Dist(int u, int v) {
return dis[u] + dis[v] - (dis[LCA(u, v)]<<1);
}
}
struct Treap {
static queue<Treap*> bin;
Treap *ls, *rs;
int val, key, cnt, size;
inline void* operator new (size_t, int _) {
Treap *re;
if(bin.size()) re = bin.front(), bin.pop();
else {
static Treap *mempool, *C; //static相当于表示全局变量
if(C == mempool) mempool = (C = new Treap[1<<16])+(1<<16); //内存不够就又开多点
re = C++;
}
re->ls = re->rs = 0x0;
re->val = _;
re->key = Rand();
re->cnt = re->size = 1;
return re;
}
inline void operator delete (void *p) {
bin.push((Treap*)p); //重载delete 回收利用
}
inline void Push_Up() {
size = cnt;
if(ls) size += ls->size;
if(rs) size += rs->size;
}
inline friend void Zig(Treap *&x) {
Treap *y = x->ls;
x->ls = y->rs;
y->rs = x; x = y;
x->rs->Push_Up();
x->Push_Up();
}
inline friend void Zag(Treap *&x) {
Treap *y = x->rs;
x->rs = y->ls;
y->ls = x; x = y;
x->ls->Push_Up();
x->Push_Up();
}
friend void Insert(Treap *&x, int y) { //插入进Treap
if(!x) { x = new(y) Treap; return; }
if(y == x->val) x->cnt++;
else if(y < x->val) {
Insert(x->ls, y);
if(x->ls->key > x->key)
Zig(x);
}
else {
Insert(x->rs, y);
if(x->rs->key > x->key)
Zag(x);
}
x->Push_Up();
}
friend void Delete(Treap *&x) { //删除以x为根的Treap子树
if(!x) return;
Delete(x->ls);
Delete(x->rs);
delete x; x = 0x0;
}
friend int Query(Treap *x, int y) {
if(!x) return 0;
if(y < x->val) return Query(x->ls, y);
else return (x->ls ? x->ls->size : 0) + x->cnt + Query(x->rs, y);
}
};
queue<Treap*> Treap::bin; //声明一下才能用
namespace Dynamic_TDC {
using namespace Gragh; //在namespace里using别的namespace...
#define alpha 0.88
#define Sit set<int>::iterator
int fa[MAXN], v[MAXN], cur;
Treap *t[MAXN], *tf[MAXN];
set<int> to[MAXN];
void Del(int x) { //删除x为根的子树
v[x] = cur;
for(Sit it = to[x].begin(); it != to[x].end(); ++it)
Del(*it), Delete(tf[*it]);
to[x].clear();
Delete(t[x]);
}
int Get_Size(int x, int ff) { //求SIZE
int re = 1;
for(int i = fir[x]; i; i = e[i].nxt)
if(v[e[i].to] == cur && e[i].ban != cur && e[i].to != ff)
re += Get_Size(e[i].to, x);
return re;
}
int Get_G(int x, int ff, int Size, int &cg) { //求重心
int re = 1; bool flag = 1;
for(int i = fir[x]; i; i = e[i].nxt)
if(v[e[i].to] == cur && e[i].ban != cur && e[i].to != ff) {
int temp = Get_G(e[i].to, x, Size, cg);
if(temp<<1 > Size) flag = 0;
re += temp;
}
if((Size-re)<<1 > Size) flag = 0;
if(flag) cg = x; return re;
}
void DFS(int x, int ff, int dpt, Treap *&p) { //将子树内的点全部插入Treap
Insert(p, dpt-r[x]);
for(int i = fir[x]; i; i = e[i].nxt)
if(v[e[i].to] == cur && e[i].ban != cur && e[i].to != ff)
DFS(e[i].to, x, dpt+e[i].len, p);
}
int TDC(int x) { //点分治
int Size = Get_Size(x, 0);
Get_G(x, 0, Size, x);
DFS(x, 0, 0, t[x]);
for(int i = fir[x]; i; i = e[i].nxt)
if(v[e[i].to] == cur && e[i].ban != cur) {
Treap *p = 0x0;
DFS(e[i].to, x, e[i].len, p); //统计子树对父亲x的贡献
e[i].ban = e[i^1].ban = cur; //打上不能再访问的标记
int temp = TDC(e[i].to);
tf[temp] = p; fa[temp] = x; to[x].insert(temp);
}
return x;
}
inline void Re_build(int x) { //重建
++cur; Del(x); int y = fa[x];
Treap *p = tf[x]; tf[x] = 0x0;
int temp = TDC(x);
fa[temp] = y;
if(y) to[y].erase(x), to[y].insert(temp); //先删边再加边
tf[temp] = p;
}
inline void Insert(int x) { //插入点分树
for(int i = x; i; i = fa[i]) {
if(fa[i]) {
int d = Dist(x, fa[i]);
lastans += Query(t[fa[i]], r[x]-d);
lastans -= Query(tf[i], r[x]-d);
Insert(tf[i], d-r[x]);
}
int D = Dist(x, i);
Insert(t[i], D-r[x]);
}
int temp = 0;//替罪咩重建
for(int i = x; fa[i]; i = fa[i])
if((double)t[i]->size / t[fa[i]]->size > alpha)
temp = fa[i];
if(temp) Re_build(temp);
}
}
int main () {
read(n), read(n);
int x, y;
for(int i = 1; i <= n; ++i) {
read(x), read(y), read(r[i]);
x ^= (lastans % mod);
Gragh::Add(i, x, y);
Gragh::Add(x, i, y);
Gragh::f[i][0] = x;
Gragh::dep[i] = Gragh::dep[x] + 1;
Gragh::dis[i] = Gragh::dis[x] + y;
Gragh::Build_LCA(i); //预处理倍增
Dynamic_TDC::to[x].insert(i);
Dynamic_TDC::fa[i] = x;
Dynamic_TDC::Insert(i);
printf("%lld\n", lastans);
}
}
看着250行抄来的代码陷入沉思
BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋 (替罪羊树 动态点分治 套 Treap)的更多相关文章
- bzoj 3435: [Wc2014]紫荆花之恋 替罪羊树维护点分治 && AC400
3435: [Wc2014]紫荆花之恋 Time Limit: 240 Sec Memory Limit: 512 MBSubmit: 159 Solved: 40[Submit][Status] ...
- BZOJ3435: [Wc2014]紫荆花之恋(替罪羊树,Treap)
Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是 ...
- luogu P3920 [WC2014]紫荆花之恋
LINK:紫荆花之恋 每次动态加入一个节点 统计 有多少个节点和当前节点的距离小于他们的权值和. 显然我们不能n^2暴力. 考虑一个简化版的问题 树已经给出 每次求某个节点和其他节点的贡献. 不难想到 ...
- BZOJ 3435: [Wc2014]紫荆花之恋
二次联通门 : BZOJ 3435: [Wc2014]紫荆花之恋 二次联通门 : luogu P3920 [WC2014]紫荆花之恋 /* luogu P3920 [WC2014]紫荆花之恋 怀疑人生 ...
- 【BZOJ3435】[Wc2014]紫荆花之恋 替罪点分树+SBT
[BZOJ3435][Wc2014]紫荆花之恋 Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从 ...
- BZOJ3435 & 洛谷3920 & UOJ55:[WC2014]紫荆花之恋
https://www.lydsy.com/JudgeOnline/problem.php?id=3435 https://www.luogu.org/problemnew/show/P3920 ht ...
- BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树)
题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...
- [WC2014]紫荆花之恋(动态点分治+替罪羊思想)
题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...
- UOJ#55 [WC2014]紫荆花之恋
题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来. 仔细看看的话,这个大树实际上是一个带权树. ...
随机推荐
- 《PC Assembly Language》读书笔记
本书下载地址:pcasm-book. 前言 8086处理器只支持实模式(real mode),不能满足安全.多任务等需求. Q:为什么实模式不安全.不支持多任务?为什么虚模式能解决这些问题? A: 以 ...
- [CF997C]Sky Full of Stars_二项式反演_等比数列_容斥原理
Sky Full of Stars 题目链接:http://codeforces.com/problemset/problem/997/C 数据范围:略. 题解: 首先考虑拟对象,如果至少有一行完全相 ...
- Oracle对象-视图和索引
Oracle 对象-视图 视图概念 视图就是提供一个查询的窗口,所有的数据来自于原表 创建视图[必须有dba权限] --查询语句创建表 create table emp as select * f ...
- 一个包含python和java环境的dockerfile
现在一个项目中遇到python调用java的jar包的环境.为了方便发布,编写了这个dockerfile,作为基础镜像. #this docker file is used to build runt ...
- HttpServletRequest对象(转)
HttpServletRequest介绍 HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供 ...
- Devexpress xaf用代码打开菜单(Navigation Item)
第一种:ViewController继承WindowController public abstract class MyWindowController : WindowController { p ...
- 【Trie】Secret Message 秘密信息
[题目链接]: https://loj.ac/problem/10054 [题意] 我认为这个题目最难的是题意: 其实分了两种情况: 1.如果当前文本串匹配不完,那么答案的是:匹配过程中遇到的模式串结 ...
- java中的exception stack有时候不输出的原因
有时候,我们在看java错误日志时,只看到一个java.lang.NullPointerException,却没有看到错误的栈,原因是启动时候有一项参数可以选择配置:OmitStackTraceInF ...
- [Vue]子组件与父组件之间传值
1.父组件与子组件传值props 1.1定义父组件,父组件传递 inputText这个数值给子组件: //父组件 //引入的add-widget组件 //使用 v-bind 的缩写语法通常更简单: & ...
- vue中非父子组件的传值bus的使用
非父子之间的组件传值,可以使用vuex.简单的状态管理,也可以用vue bus vue bus可以实现不同组件间.不同页面间的通信,比如我在A页面出发点击事件,要B页面发生变化,使用方法如下: 全局定 ...