• 牛客练习赛42E 热爆了(Lct+树状数组)


    牛客练习赛42E 热爆了(Lct+树状数组)

    解题思路

    小 X 决定出一道送温暖题来和大家一起愉悦
    他给了你一棵 n 个节点的树,每个点有个点权
    ai
    现在他给了你 Q 个询问,每次会给定 L,R ,然后定义满足
    ai∈[L,R] 的点 i 为关键点
    你需要回答出满足下列至少一个条件的点 x 的个数:1. x 是关键点2. 在树上删去 x 和所有与其相连的边后,存在两个关键点 a,b ,使得 a 和 b 不连通

    数据范围

    [1 le n le 10^5 ]

    解题思路

    神仙 ztb 学长给出了一个神仙的线段树合并做法,我想了想有一共点分树 + lct + 树状数组的方法,又想了一下,点分树完全可以扔了,下面给出一个 (Theta(nlog n)) 的 LCT + 树状数组做法吧

    这道题讲的就是求一个包含所有关键点的联通块大小

    有两个套路如果知道那么这题将会很好想到

    第一个套路:找到 ([L,R]) 所有点的 (Lca),易知 (Lca) 肯定在联通块内,且除了 (Lca) 子树的节点不会在联通块内,也就是我们直接统计 ([L,R])(Lca) 的路径之并的大小即可,一开始我想建个点分树,结果发现直接统计 ([L,R]) 到根的路径再将 (Lca) 到根的路径减去即可,求 (Lca) 可以用三个 st 表实现

    第二个套路:将所有询问离线下来,按 R 排序,我们把所有贡献尽可能的放到靠右的点上,具体来说,用个树状数组维护,加入 R 时,把 R 到根节点的路径长度加到 R 位置上,但这条路径可能会和以前的路径有重合,要把以前的贡献删掉,容易发现用 LCT 可以很好的维护这个问题

    所以我们较为简单的实现了这题

    #pragma GCC optimize(3, "inline")
    #include <queue>
    #include <vector>
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define MP make_pair
    #define ll long long
    #define fi first
    #define se second
    using namespace std;
    
    template <typename T>
    void read(T &x) {
        x = 0; bool f = 0;
        char c = getchar();
        for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
        for (;isdigit(c);c=getchar()) x=x*10+(c^48);
        if (f) x=-x;
    }
    
    template<typename F>
    inline void write(F x, char ed = '
    ')
    {
    	static short st[30];short tp=0;
    	if(x<0) putchar('-'),x=-x;
    	do st[++tp]=x%10,x/=10; while(x);
    	while(tp) putchar('0'|st[tp--]);
    	putchar(ed);
    }
    
    template <typename T>
    inline void Mx(T &x, T y) { x < y && (x = y); }
    
    template <typename T>
    inline void Mn(T &x, T y) { x > y && (x = y); }
    
    const int N = 200500;
    
    int cnt[N], a[N], c[N];
    int lg[N], st[N][18], d[N], n;
    
    inline void Add(int x, int c) {
    	for (; x <= n; x += x & -x) d[x] += c;
    }
    
    inline int query(int x) {
    	int res = 0;
    	for (; x; x -= x & -x) res += d[x];
    	return res;
    }
    
    namespace LCT {
    	#define ls son[x][0]
    	#define rs son[x][1]
    	int son[N][2], f[N], siz[N], col[N];
    	bool nroot(int x) {
    		return son[f[x]][0] == x || son[f[x]][1] == x;
    	}
    	void update(int x) {
    		siz[x] = siz[ls] + siz[rs] + 1;
    	}
    	void rotate(int x) {
    		int y = f[x], z = f[y];
    		int k = son[y][1] == x, w = son[x][!k];
    		if (nroot(y)) son[z][son[z][1]==y] = x; f[x] = z;
    		f[y] = x, son[x][!k] = y;
    		if (w) f[w] = y; son[y][k] = w;
    		update(y);
    	}
    	void spread(int x) {
    		if (ls) col[ls] = col[x];
    		if (rs) col[rs] = col[x];
    	}
    	int st[N];
    	void splay(int x) {
    		int y = x, z = 0; st[++z] = y;
    		while (nroot(y)) st[++z] = y = f[y];
    		while (z) spread(st[z--]);
    		while (nroot(x)) {
    			int y = f[x], z = f[y];
    			if (nroot(y)) {
    				if ((son[z][0] == y) ^ (son[y][0] == x)) rotate(x);
    				else rotate(y);
    			}
    			rotate(x);
    		}
    		update(x);
    	}
    	void access(int x, int i) {
    		for (int y = 0; x; x = f[y = x]) {
    			splay(x); siz[x] -= siz[rs], (rs = y) && (f[y] = x);
    			if (col[x]) Add(col[x], -siz[x]);
    			Add(i, siz[x]), update(x); col[x] = i;
    		}
    	}
    }
    
    struct St1 {
    	int st[N<<1][19], n;
    	void init(void) {
    		for (int j = 1;j <= 20; j++) 
    			for (int i = 1;i + (1 << j) - 1 <= n; i++)
    				st[i][j] = min(st[i][j-1], st[i+(1<<(j-1))][j-1]);
    	}
    	
    	inline int query(int l, int r) {
    		int t = lg[r - l + 1];
    		return min(st[l][t], st[r-(1<<t)+1][t]);
    	}
    
    }s1, s2;
    
    struct St2 {
    	int st[N][19], n;
    	void init(void) {
    		for (int j = 1;j <= 20; j++) 
    			for (int i = 1;i + (1 << j) - 1 <= n; i++)
    				st[i][j] = max(st[i][j-1], st[i+(1<<(j-1))][j-1]);
    	}
    	
    	inline int query(int l, int r) {
    		int t = lg[r - l + 1];
    		return max(st[l][t], st[r-(1<<t)+1][t]);
    	}
    
    }s3;
    
    int dep[N], ne[N<<1], to[N<<1], h[N], tot, cc;
    inline void add(int x, int y) {
    	ne[++tot] = h[x], to[h[x] = tot] = y;
    }
    
    void dfs(int x, int fa) {
    	LCT::f[x] = fa, LCT::siz[x] = 1;
    	s2.st[++cc][0] = dep[x], s1.st[a[x]][0] = s3.st[a[x]][0] = cc;
    	for (int i = h[x]; i; i = ne[i]) {
    		int y = to[i]; if (y == fa) continue;
    		dep[y] = dep[x] + 1, dfs(y, x);
    		s2.st[++cc][0] = dep[x];
    	}
    }
    
    int Dep(int l, int r) {
    	int x = s1.query(l, r), y = s3.query(l, r);
    	return s2.query(x, y);
    }
    
    vector<pair<int, int> > v[N]; 
    int pos[N], ans[N<<2], q;
    
    int main() {
    //	freopen ("hs.in","r",stdin);
    //	freopen ("hs.out","w",stdout);
    	read(n), read(q);
    	for (int i = 1;i <= n; i++) read(a[i]), c[i] = a[i];
    	sort(c + 1, c + n + 1);
    	for (int i = 1;i <= n; i++) {
    		a[i] = lower_bound(c + 1, c + n + 1, a[i]) - c;
    		cnt[a[i]]++, a[i] += cnt[a[i]] - 1, pos[a[i]] = i;
    	}
    	for (int i = 1, x, y;i < n; i++) 
    		read(x), read(y), add(x, y), add(y, x);
    	dfs(1, 0), s2.n = cc, s1.n = s3.n = n;
    	for (int i = 2;i <= cc; i++) lg[i] = lg[i>>1] + 1;
    	s1.init(), s2.init(), s3.init();
    	for (int i = 1, l, r;i <= q; i++) {
    		read(l), read(r);
    		l = lower_bound(c + 1, c + n + 1, l) - c;
    		r = upper_bound(c + 1, c + n + 1, r) - c - 1;
    		if (l > r) continue; ans[i] -= Dep(l, r);
    		v[r].emplace_back(l, i);
    	}
    	for (int i = 1;i <= n; i++) {
    		LCT::access(pos[i], i);
    		for (auto t: v[i]) 
    			ans[t.se] += query(i) - query(t.fi - 1);
    	}
    	for (int i = 1;i <= q; i++) write(ans[i]);
    	return 0;
    }
    
    
  • 相关阅读:
    inet_ntoa 的一个小问题
    获取DNS服务器的版本信息
    host_network_interfaces_slow_mode_thresholds
    10月8日至11月底考试安排
    腾讯广点通防作弊
    移动广告作弊方式及防范方式
    广告联盟常用的防作弊手续
    移动端点击作弊与激活作弊的现象与预警
    数据科学家最常用的十种算法(我准备拿这个当成学习参考)
    项目的命名规范,为以后的程序开发中养成良好的行为习惯
  • 原文地址:https://www.cnblogs.com/Hs-black/p/13399656.html
Copyright © 2020-2023  润新知