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


    题意

    分析

    引用PoPoQQQ的话

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

    (蒟蒻狂笑)

    Orz PoPoQQQ,我又抄PoPoQQQ的题解了

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

    这种符合某条件的点对,首先就想到点分治…先假设树的形态是固定的,我们考虑满足dis(i,j)<=ri+rjdis(i,j)<=r_i+r_j的点对,假设它们在点分树上的lcalcauu,定义did_iiiuu的距离.那么有di+dj<=ri+rjdiri<=rjdjd_i+d_j<=r_i+r_j\d_i-r_i<=r_j-d_j 那么我们将子树中所有dirid_i-r_i插入平衡树,只需要查询rjdjr_j-d_j在平衡树中的排名就能查询和jj组成点对的ii的数量.

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

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

    setset存边挺省事的,就是比较慢…

    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
    ", lastans);
    	}
    }
    

    看着250行抄来的代码陷入沉思

  • 相关阅读:
    RDA安装
    关闭SELinux
    Linux下开启关闭防火墙
    长事务管理
    增加复制表
    Python使用property函数定义的属性名与其他实例变量重名会怎么样?
    Python使用property函数定义属性访问方法如果不定义fget会怎么样?
    Python使用property函数和使用@property装饰器定义属性访问方法的异同点分析
    第8.18节 Python类中内置析构方法__del__
    Python中的列表解析和列表推导是一回事吗?
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039366.html
Copyright © 2020-2023  润新知