BZOJ 3435 / Luogu 3920 [WC2014]紫荆花之恋 (替罪羊树 动态点分治 套 Treap)

版权声明:本人版权意识薄弱,请随意转载 https://blog.csdn.net/Ike940067893/article/details/88748933

题意

分析

引用PoPoQQQ的话

吾辈有生之年终于把这道题切了。。。QAQ

(蒟蒻狂笑)

Orz PoPoQQQ,我又抄PoPoQQQ的题解啦

感觉看了PoPoQQQ的代码,腰也不酸了,腿也不疼了,动态树分治Treap替罪咩啥的全都会啦

正经的…

  • 突然发现有旋Treap没那么难写
  • 学习了一波C++语法,第一次用指针,什么 n e w / d e l e t e new/delete 也大概会用了…(这玩意还能重载…跪了)
  • 学习了一波点分治的正确写法.我之前写的都是什么烂玩意儿
  • dalao代码的细节处理得好啊,学习了学习了 (这就是你抄代码的原因?)

这种符合某条件的点对,首先就想到点分治…先假设树的形态是固定的,我们考虑满足 d i s ( i , j ) < = r i + r j dis(i,j)<=r_i+r_j 的点对,假设它们在点分树上的 l c a lca u u ,定义 d i d_i i i u u 的距离.那么有 d i + d j < = r i + r j d i r i < = r j d j d_i+d_j<=r_i+r_j\\d_i-r_i<=r_j-d_j 那么我们将子树中所有 d i r i d_i-r_i 插入平衡树,只需要查询 r j d j r_j-d_j 在平衡树中的排名就能查询和 j j 组成点对的 i i 的数量.

由于树的形态不确定,那么就动态点分治就行了.那么我们像点分治常见的套路,在点分树上的每一个点维护两颗平衡树,一棵 t t 表示子树内的所有点 d i r i d_i-r_i 组成的平衡树,一棵 t f tf 维护这一棵子树对父亲的贡献.那么查询一个点的时候,从那个点往上计算,每一次用父亲的 t t 所查询的值减去这棵子树的 t f tf 所查询的值 就能不重不漏地算出答案(因为要去除点对在同一棵子树内的情况).然后修改可以和查询在同一个函数内进行,先查后修改.

由于动态点分治可能会出现一条链的情况,那么要像替罪羊树的思想,子树不平衡就重构…

s e t set 存边挺省事的,就是比较慢…

扫描二维码关注公众号,回复: 5649467 查看本文章

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
	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行抄来的代码陷入沉思

猜你喜欢

转载自blog.csdn.net/Ike940067893/article/details/88748933