zoukankan      html  css  js  c++  java
  • Codeforces 1214 F G H 补题记录

    翻开以前打的 #583,水平不够场上只过了五题。最近来补一下题,来记录我sb的调试过程。

    估计我这个水平现场也过不了,因为前面的题已经zz调了好久……


    F:就是给你环上一些点,两两配对求距离最小值。

    匹配边明显是不能交叉的啊。那就直接转一个环就好了。

    考虑计算贡献,盲猜一个点的贡献方向改变不会特别多,观看题解以验证,证明一发,发现猜对了。

    很明显,考虑 (x leq y),那么固定 (y),一定有一个分界点,使得 (y - x > m - x + y)

    开一个长度为 (n) 的数组,表示转 (frac{2 pi i}{n})

    所以对于每个点计算一下贡献,对于一个连续段贡献相同,直接差分前缀和就行。

    边界还是要仔细考虑的,也就是 (y - x = m - x + y) 的时候。分别考虑两个环贡献,具有对偶性,那么写成一个函数时就要注意边界。

    细节也不多,过了样例就A了。

    #include <bits/stdc++.h>
     
    const int MAXN = 200010;
    typedef long long LL;
    int n, R;
    struct node {
    	LL at; int id;
    	bool operator < (node b) const { return at < b.at; }
    } as[MAXN], bs[MAXN];
    int ansl[MAXN];
    LL pre1[MAXN], pre2[MAXN], * pre;
    void _mkadd(int l, int r, int v) {
    	pre[l] += v, pre[r + 1] -= v;
    }
    void mkadd(int l, int r, int v, int dta) {
    	if (l >= r) return ;
    	--r;
    	l = (l - 1 - dta + n) % n + 1;
    	r = (r - 1 - dta + n) % n + 1;
    	if (l <= r) _mkadd(l, r, v);
    	else _mkadd(1, r, v), _mkadd(l, n, v);
    }
    void pusharr(int at, node * dst, int dta) {
    	node * it;
    	if (dst == bs)
    		it = std::lower_bound(dst + 1, dst + 1 + n, (node) {at << 1, 0});
    	else
    		it = std::upper_bound(dst + 1, dst + 1 + n, (node) {at << 1, 0});
    	node * lhs = std::upper_bound(dst + 1, it, (node) {2 * at - R, 0});
    	node * rhs = std::lower_bound(it, dst + 1 + n, (node) {2ll * at + R, 0});
    	const int delta = (dst != as) * R;
    	mkadd(1, lhs - dst, delta - at, dta);
    	mkadd(lhs - dst, it - dst, at, dta);
    	mkadd(it - dst, rhs - dst, -at, dta);
    	mkadd(rhs - dst, 1 + n, delta + at, dta);
    }
    int main() {
    	std::ios_base::sync_with_stdio(false), std::cin.tie(0);
    	std::cin >> R >> n;
    	for (int i = 1; i <= n; ++i) as[i].id = i, std::cin >> as[i].at;
    	for (int i = 1; i <= n; ++i) bs[i].id = i, std::cin >> bs[i].at;
    	std::sort(as + 1, as + 1 + n);
    	std::sort(bs + 1, bs + 1 + n);
    	for (int i = 1; i <= n; ++i) as[i].at <<= 1, bs[i].at <<= 1;
    	for (int i = 1; i <= n; ++i) {
    		pre = pre1, pusharr(as[i].at >> 1, bs, i - 1);
    		pre = pre2, pusharr(bs[i].at >> 1, as, i - 1);
    	}
    	LL ans = 0x3f3f3f3f3f3f3f3fLL; int at = 1;
    	for (int i = 1; i <= n; ++i)
    		pre1[i] += pre1[i - 1], pre2[i] += pre2[i - 1];
    	for (int i = 1; i <= n; ++i) {
    		LL now = pre1[i] + pre2[i > 1 ? n - i + 2 : 1];
    		if (now < ans) ans = now, at = i;
    	}
    	for (int i = 1; i <= n; ++i)
    		ansl[as[i].id] = bs[(i + at - 2) % n + 1].id;
    	std::cout << ans << std::endl;
    	for (int i = 1; i <= n; ++i)
    		std::cout << ansl[i] << ' ';
    	std::cout << std::endl;
    	return 0;
    }
    

    G:询问是否存在一个矩形的四个顶点,对角相等,邻边相反的点,带一行的区间修改。

    看到数据范围,(m imes Q = 10^9),自然 bitset。但是怎么维护呢?

    考虑两行,记 (A) 为其中一行 (1) 出现的位置, (B) 是另一行的 (1),所有位置(全集)就是 (U)

    那么就是 (A cap left(U ackslash B ight))(B cap left(U ackslash A ight)) 都非空,这个判定函数记为 (f(A, B) = [A cap left(U ackslash B ight) eq emptyset][B cap left(U ackslash A ight) eq emptyset])

    根据题解( eg fleft(A,B ight)) 具有传递性。其实这个是集合包含啦,如果两个集合互不包含,就会有解。(我太菜没从上面想到包含关系)

    那么显然无解时包含关系形成了一条链。我们直接按集合大小排序,然后维护相邻不包含关系的集合即可。

    手写bitset写挂……貌似效率还没stl高。

    #include <bits/stdc++.h>
     
    const int MAXN = 2000;
    typedef long long LL;
    int n, m, Q;
    const int dig = 2048 / 64;
    struct bitset {
    	LL A[dig]; int ppc;
    	bool contains(const bitset & b) const {
    		for (int i = 0; i != dig; ++i)
    			if ((A[i] & b.A[i]) != b.A[i])
    				return false;
    		return true;
    	}
    	void rev(int l, int r) {
    		for (int i = (l >> 6); i <= (r >> 6); ++i)
    			ppc -= __builtin_popcountll(A[i]);
    		A[l >> 6] ^= ((1ll << (l & 63)) - 1) ^ -1;
    		A[r >> 6] ^= (r & 63) == 63 ? -1 : (1ll << ((r & 63) + 1)) - 1;
    		(l >> 6) == (r >> 6) ? (A[l >> 6] ^= -1) : 0;
    		for (int i = (l >> 6) + 1; i < (r >> 6); ++i) A[i] ^= -1;
    		for (int i = (l >> 6); i <= (r >> 6); ++i)
    			ppc += __builtin_popcountll(A[i]);
    	}
    	int ctzofandinvb(const bitset & b) {
    		for (int i = 0; i != dig; ++i) {
    			LL now = A[i] & ~b.A[i];
    			if (now) return i << 6 | __builtin_ctzll(now);
    		}
    		return -1;
    	}
    } mat[MAXN];
    typedef std::pair<int, int> PII;
    std::set<PII> S, diff;
    int getsuc(std::set<PII>::iterator it) {
    	return ++it != S.end() ? it -> second : -1;
    }
    int getpre(std::set<PII>::iterator it) {
    	return it != S.begin() ? (--it) -> second : -1;
    }
    void ins(int a, int b) {
    	if (a == -1 || b == -1) return ;
    	if (!mat[b].contains(mat[a]))
    		diff.insert(PII(a, b));
    }
    void del(int a, int b) {
    	if (a == -1 || b == -1) return ;
    	diff.erase(PII(a, b));
    }
    bitset t;
    int main() {
    	std::ios_base::sync_with_stdio(false), std::cin.tie(0);
    	std::cin >> n >> m >> Q;
    	for (int i = 0; i < n; ++i) S.insert(PII(0, i));
    	int t1, t2, t3;
    	while (Q --> 0) {
    		std::cin >> t1 >> t2 >> t3; --t1, --t2, --t3;
    		auto t = S.find(PII(mat[t1].ppc, t1));
    		int suc = getsuc(t), pre = getpre(t);
    		del(pre, t1); del(t1, suc);
    		S.erase(t); mat[t1].rev(t2, t3);
    		auto at = S.insert(PII(mat[t1].ppc, t1)).first;
    		int ts = getsuc(at), tp = getpre(at); 
    		del(tp, ts);
    		ins(tp, t1); ins(t1, ts);
    		if (ts != suc && tp != pre) ins(pre, suc);
    		if (diff.empty()) std::cout << "-1
    ";
    		else {
    			PII t = *diff.begin();
    			int x1 = t.first, x2 = t.second;
    			int y1 = mat[x1].ctzofandinvb(mat[x2]);
    			int y2 = mat[x2].ctzofandinvb(mat[x1]);
    			if (x1 > x2) std::swap(x1, x2);
    			if (y1 > y2) std::swap(y1, y2);
    			++x1, ++x2, ++y1, ++y2;
    			std::cout << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << '
    ';
    		}
    	}
    	return 0;
    }
    

    H:给树 (K) 染色,使得所有长为 (K) 的路径恰好包含所有 (K) 种颜色。

    一道简单题,但我挂了一堆 sb 地方

    显然,应该找出直径来。然后直径上插入一些子树,根据在直径上的位置调整颜色。颜色就从上往下按顺序分配。

    有了这个,我们考虑无解情况:

    如果在直径上,从左边和右边都能下来,那么直径必须回文。

    所以先特判掉 (K = 2) 的情况,剩下直径肯定不回文。

    乍一看这个判定太弱了。我们考虑颜色分配,祖先链分配下来分叉成两个子树,两个子树的链又能拼成长度为 (K) 的链,那这链必须存在回文,必定无解。

    也就是存在一个点,往三个方向 (A, B, C)(AB, BC, AC) 三个方向的链长度都大于等于 (K)

    那么最后的问题就是考虑直径上怎么下去了。

    如果能从右边下来,那么编号分配的 (delta = -1),否则就是 (1)

    据说我直径求完没加 (1) 导致少一个点,还有 dp 时在根加入了没用的子树导致 (No) 判挂,取模有两处应该写 (K) 结果写了 (R)……

    实际上细节不多。

    #include <bits/stdc++.h>
     
    const int MAXN = 200010;
    void bye() { std::cout << "No
    "; exit(0); }
    int head[MAXN], nxt[MAXN << 1], to[MAXN << 1], tot;
    void addedge(int b, int e) {
    	nxt[++tot] = head[b]; to[head[b] = tot] = e;
    	nxt[++tot] = head[e]; to[head[e] = tot] = b;
    }
    int ansl[MAXN];
    int n, K;
    void simpledfs(int u, int fa = 0, int col = 1) {
    	ansl[u] = col;
    	for (int i = head[u]; i; i = nxt[i])
    		if (to[i] != fa)
    			simpledfs(to[i], u, col ^ 1);
    }
    int dis[MAXN];
    void dfsx(int u, int fa = 0) {
    	for (int i = head[u]; i; i = nxt[i])
    		if (to[i] != fa) {
    			dis[to[i]] = dis[u] + 1;
    			dfsx(to[i], u);
    		}
    }
    const int INF = 0x3f3f3f3f;
    struct data {
    	int a[3];
    	data() { a[0] = a[1] = a[2] = -INF; }
    	void insert(int x) {
    		if (x >= a[0]) a[2] = a[1], a[1] = a[0], a[0] = x;
    		else if (x >= a[1]) a[2] = a[1], a[1] = x;
    		else if (x >= a[2]) a[2] = x;
    	}
    	void shift() { ++a[0], ++a[1], ++a[2]; }
    } val[MAXN], vt[MAXN];
    void dfs1(int u, int fa = 0) {
    	for (int i = head[u]; i; i = nxt[i])
    		if (to[i] != fa) {
    			dfs1(to[i], u);
    			val[u].insert(std::max(val[to[i]].a[0], 0) + 1);
    		}
    }
    void dfs2(int u, int fa = 0, int upv = 0) {
    	for (int i = head[u]; i; i = nxt[i])
    		if (to[i] != fa) {
    			int tv = std::max(upv, val[u].a[val[to[i]].a[0] + 1 == val[u].a[0]]) + 1;
    			dfs2(to[i], u, tv);
    		}
    	vt[u] = val[u]; if (fa) vt[u].insert(upv);
    	if (vt[u].a[2] + vt[u].a[1] + 1 >= K) bye();
    }
    int li[MAXN], inc[MAXN];
    bool mark(int u, int dst, int fa = 0) {
    	if (u == dst) {
    		li[dis[u]] = u;
    		return true;
    	}
    	for (int i = head[u]; i; i = nxt[i])
    		if (to[i] != fa) {
    			if (mark(to[i], dst, u)) {
    				li[dis[u]] = u;
    				return true;
    			}
    		}
    	return false;
    }
    int R;
    void dfsmk(int u, int fa, int now, int delta) {
    	now = (now + delta) % K;
    	ansl[u] = now;
    	for (int i = head[u]; i; i = nxt[i])
    		if (to[i] != fa)
    			dfsmk(to[i], u, now, delta);
    }
    int main() {
    	std::ios_base::sync_with_stdio(false), std::cin.tie(0);
    	std::cin >> n >> K;
    	for (int i = 1, t1, t2; i < n; ++i) {
    		std::cin >> t1 >> t2;
    		addedge(t1, t2);
    	}
    	if (K == 2) simpledfs(1);
    	else {
    		dfs1(1); dfs2(1);
    		dis[1] = 0; dfsx(1);
    		int at = 1, rt;
    		for (int i = 2; i <= n; ++i) if (dis[i] > dis[at]) at = i;
    		dis[at] = 0; dfsx(rt = at);
    		at = 1;
    		for (int i = 2; i <= n; ++i) if (dis[i] > dis[at]) at = i;
    		R = dis[at] + 1;
    		mark(rt, at);
    		for (int i = 0; i < R; ++i) inc[li[i]] = true;
    		for (int i = 0; i < R; ++i) {
    			ansl[li[i]] = i % K;
    			int dta = R - 1 - i > i ? K - 1 : 1;
    			for (int j = head[li[i]]; j; j = nxt[j])
    				if (!inc[to[j]])
    					dfsmk(to[j], li[i], i, dta);
    		}
    	}
    	std::cout << "Yes" << std::endl;
    	for (int i = 1; i <= n; ++i) std::cout << ansl[i] + 1 << ' ';
    	std::cout << std::endl;
    	return 0;
    }
    
  • 相关阅读:
    [HDU1561]The more, The Better
    [洛谷P1352][codevs1380]没有上司的舞会
    【51Nod1773】A国的贸易 解题报告
    快速沃尔什变换
    【SDOI2015】序列统计 解题报告
    【CF438E】小朋友和二叉树 解题报告
    多项式Ⅰ
    洛谷 P5105 不强制在线的动态快速排序
    【BZOJ4916】神犇和蒟蒻 解题报告
    【BZOJ3309】DZY Loves Math 解题报告
  • 原文地址:https://www.cnblogs.com/daklqw/p/11692692.html
Copyright © 2011-2022 走看看