zoukankan      html  css  js  c++  java
  • bzoj4129 Haruna’s Breakfast 树上带修莫队+分块

    题目传送门

    https://lydsy.com/JudgeOnline/problem.php?id=4129

    题解

    考虑没有修改的序列上的版本应该怎么做:

    弱化的题目应该是这样的:

    给定一个序列,每次询问区间 ([l, r]) 中元素的最小没有出现的自然数。


    这个弱化的版本可以用离线+线段树二分水掉。但是这个做法显然不太好搬到树上做。

    上面的弱化版还有一个莫队做法:可以用莫队维护出来每一个区间的每一个数的出现为次数。把出现过的数通过分块表示出来,于是查询的时候枚举每一个块,寻找第一个不满的块。


    那么放到树上以后,就用树上莫队吧。

    既然还有修改,那么就是树上带修莫队。


    注意一下:

    因为数据范围比较大,所以需要离散化。但是离散化的时候为了防止漏掉一些数(某两个数在数轴上是不相邻的,但是在离散化以后相邻),需要把出现过的每一个数 (+1) 放进被离散化的序列中。


    UPD:

    貌似不需要离散化,答案一定 (< n),所以只需要保留 (<n) 的数就可以了。

    我真的有点智商低下。

    一定要考虑题目中的一些量是否有用,是否可以简化。


    时间复杂度 (O(mcdot n^{frac 23}))

    #include<bits/stdc++.h>
    
    #define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
    #define dbg(...) fprintf(stderr, __VA_ARGS__)
    #define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
    #define fi first
    #define se second
    #define pb push_back
    
    template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
    template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
    
    typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
    
    template<typename I> inline void read(I &x) {
    	int f = 0, c;
    	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    	x = c & 15;
    	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    	f ? x = -x : 0;
    }
    
    const int N = 5e4 + 7;
    const int B = 200 + 7;
    
    #define bl(x) (((x) - 1) / blo + 1)
    #define st(x) (((x) - 1) * blo + 1)
    #define ed(x) std::min((x) * blo, dis)
    
    int n, m, dis, dfc, dfc2, m1, m2, blo, now;
    int a[N], b[N << 2], ans[N];
    int dep[N], f[N], siz[N], son[N], dfn[N], pre[N], top[N];
    int ldfn[N], rdfn[N], seq[N << 1];
    int used[N], s[N << 2], ss[B], cnt[N << 2];
    
    struct Update { int id, x, k, pre; } u[N];
    struct Query {
    	int id, l, r, lca, *ans;
    	inline bool operator < (const Query &b) const {
    		if (bl(l) != bl(b.l)) return l < b.l;
    		if (bl(r) != bl(b.r)) return r < b.r;
    		return id < b.id;
    	}
    } q[N];
    
    struct Edge { int to, ne; } g[N << 1]; int head[N], tot;
    inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
    inline void adde(int x, int y) { addedge(x, y), addedge(y, x); }
    
    inline void dfs1(int x, int fa = 0) {
    	dep[x] = dep[fa] + 1, f[x] = fa, siz[x] = 1;
    	for fec(i, x, y) if (y != fa) dfs1(y, x), siz[x] += siz[y], siz[y] > siz[son[x]] && (son[x] = y);
    }
    inline void dfs2(int x, int pa) {
    	top[x] = pa, dfn[x] = ++dfc, pre[dfc] = x;
    	ldfn[x] = ++dfc2, seq[dfc2] = x;
    	if (!son[x]) return (void)(rdfn[x] = ++dfc2, seq[dfc2] = x); dfs2(son[x], pa);
    	for fec(i, x, y) if (y != f[x] && y != son[x]) dfs2(y, y);
    	rdfn[x] = ++dfc2, seq[dfc2] = x;
    }
    inline int lca(int x, int y) {
    	while (top[x] != top[y]) dep[top[x]] > dep[top[y]] ? x = f[top[x]] : y = f[top[y]];
    	return dep[x] < dep[y] ? x : y;
    }
    
    inline void qadd(int x, int k) {
    	s[x] += k, ss[bl(x)] += k;
    	assert(s[x] == 0 || s[x] == 1);
    	assert(ss[bl(x)] <= ed(bl(x)) - st(bl(x)) + 1 && ss[bl(x)] >= 0);
    }
    inline int qans() {
    	for (int i = 1; i <= bl(dis); ++i) if (ss[i] != ed(i) - st(i) + 1)
    		for (int j = st(i); j <= ed(i); ++j) if (!s[j]) return b[j - 1] + 1;
    	return b[dis] + 1;
    }
    
    inline void madd(int x) {
    	++cnt[x];
    	if (cnt[x] == 1) qadd(x, 1);
    }
    inline void mdel(int x) {
    	--cnt[x];
    	if (cnt[x] == 0) qadd(x, -1);
    }
    inline void mupd(int x) {
    	if (used[x]) mdel(a[x]);
    	else madd(a[x]);
    	used[x] ^= 1;
    }
    
    inline void cadd(int i) {
    	int x = u[i].x, k = u[i].k;
    	if (used[x]) mdel(a[x]);
    	u[i].pre = a[x], a[x] = k;
    	if (used[x]) madd(a[x]);
    }
    inline void cdel(int i) {
    	int x = u[i].x;
    	if (used[x]) mdel(a[x]);
    	assert(a[x] == u[i].k);
    	a[x] = u[i].pre;
    	if (used[x]) madd(a[x]);
    }
    inline void cupd(int i) {
    	while (now < m1 && u[now + 1].id <= i) cadd(++now);
    	while (now && u[now].id > i) cdel(now--);
    }
    
    inline void lsh() {
    	for (int i = 1; i <= dis; ++i) b[i + dis] = b[i] + 1;
    	dis *= 2;
    	std::sort(b + 1, b + dis + 1);
    	dis = std::unique(b + 1, b + dis + 1) - b - 1;
    	for (int i = 1; i <= n; ++i) a[i] = std::lower_bound(b + 1, b + dis + 1, a[i]) - b;
    	for (int i = 1; i <= m1; ++i) u[i].k = std::lower_bound(b + 1, b + dis + 1, u[i].k) - b;
    	b[0] = -1;
    }
    
    inline void work() {
    	lsh();
    	blo = pow(n, 2.0 / 3);
    	std::sort(q + 1, q + m2 + 1);
    	int l = 1, r = 0;
    	for (int i = 1; i <= m2; ++i) {
    		cupd(q[i].id);
    		while (l > q[i].l) mupd(seq[--l]);
    		while (r < q[i].r) mupd(seq[++r]);
    		while (l < q[i].l) mupd(seq[l++]);
    		while (r > q[i].r) mupd(seq[r--]);
    		if (q[i].lca) assert(!used[q[i].lca]);
    		if (q[i].lca) mupd(q[i].lca);
    		*q[i].ans = qans();
    		if (q[i].lca) mupd(q[i].lca);
    	}
    	for (int i = 1; i <= m2; ++i) printf("%d
    ", ans[i]);
    }
    
    inline void init() {
    	read(n), read(m);
    	for (int i = 1; i <= n; ++i) read(a[i]), b[++dis] = a[i];
    	int x, y;
    	for (int i = 1; i < n; ++i) read(x), read(y), adde(x, y);
    	dfs1(1), dfs2(1, 1);
    	for (int i = 1; i <= m; ++i) {
    		int opt;
    		read(opt);
    		if (opt == 0) ++m1, read(u[m1].x), read(u[m1].k), u[m1].id = i, b[++dis] = u[m1].k;
    		else {
    			int x, y, p;
    			read(x), read(y);
    			if (ldfn[x] > ldfn[y]) std::swap(x, y);
    			p = lca(x, y), ++m2;
    			if (p == x) q[m2] = (Query){ i, ldfn[x], ldfn[y], 0, ans + m2 };
    			else q[m2] = (Query){ i, rdfn[x], ldfn[y], p, ans + m2 };
    		}
    	}
    }
    
    int main() {
    #ifdef hzhkk
    	freopen("hkk.in", "r", stdin);
    #endif
    	init();
    	work();
    	fclose(stdin), fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    Android中使用HttpURLConnection实现GET POST JSON数据与下载图片
    BZOJ 1293 SCOI2009 生日礼物 堆
    Git小玩
    VM tools安装错误The path &quot;&quot; is not a valid path to the xx generic kernel headers.
    css hover对其包括的元素进行样式设置
    C#高级编程---暂停计划
    如何配置和使用Tomcat访问日志
    关于性能测试应该知道的一些事(转载)
    Linux 平台如何查看某个进程的线程数?
    《让LoadRunner走下神坛》
  • 原文地址:https://www.cnblogs.com/hankeke/p/bzoj4129.html
Copyright © 2011-2022 走看看