zoukankan      html  css  js  c++  java
  • [LOJ3274]变色龙之恋

    可恶!就差……一步了…… 【反贼阵亡】

    最近做题好像经常这样,只差最后一步想不出来(x

    首先考虑一个 (O(n^2)) 次交互的做法:将每对 ((i, j)) 询问一次,如果返回值为 (1),只会有以下几种情况:

    1. (i, j) 颜色相同;
    2. (i) 喜欢 (j)
    3. (j) 喜欢 (i)

    将所有对询问一次之后,对返回值为 (1)((i, j)) 之间连边,发现对于一个 (i),一定会恰有 (1)(3) 条边。恰有 (1) 条边的情况是喜欢关系形成了二元环,在询问时相互抵消了(我酸了),因此这样的点颜色可以直接确定。

    对于恰有 (3) 条边的情况,设分别为 ((i, v_0), (i, v_1), (i, v_2))。依次询问 ({i, v_0, v_1, v_2} setminus {v_i}),发现只有 (v_i) 恰好是上面的情况 (2) 的时候,返回值为 (1)

    因此,我们唯一确定了 (i) 喜欢的是谁,只要再知道喜欢 (i) 的是谁,就可以求出 (i) 的颜色了。

    发现这种单向的喜欢关系必然是若干个环组成的(然而现实生活中只会有内向树),只需要顺着 (v_i) 向下递归即可。整个递归过程中,环上的每个点都可以一次询问求出喜欢的是谁和对应的颜色,最终回到 (i) 时,也可以求出 (i)。这个过程是 (O(n)) 的,不超过 (3n) 次询问即可。

    现在唯一的问题是如何找到每个点的连边。再考虑一下每个点预先知道性别的情况:

    (S={i| y_i=0}),现在考虑对于每个 (y_i=1)(i),求出它与 (S) 中点的连边,考虑二分实现。

    具体来说,设 (operatorname{solve}(S', x)) 表示查找集合 (S')(x) 之间的连边,若询问 (S'cup {x}) 时,返回值为 (|S'|+1),则 (S') 内部元素与 (x) 显然没有连边,可以直接返回。否则,若 (|S'|=1),可以直接将对应的点与 (x) 连边。否则,直接将 (S') 分为尽量相等的两部分,递归处理。通过精细实现和一些剪枝,可以做到不超过 (3nlog_2 n) 次询问。

    然后我就不会了(少女百度中……

    由于所有边都存在于异性之间,因此上面知道性别的意义,实际上在于提前确定了二分图中每个点的归属。

    实际上知道每个点在二分图的哪边并没有多少用,我们关心的只是,(operatorname{solve}(S', x)) 中,(S') 内部没有连边。因此我们可以从 (1)(2n) 扫描,对每个 (i),求出所有 ((i, j), j<i)。初始时,由于未确定任何连边,可以给每个点任意指定颜色;每次连边时,暴力将不对的颜色修改过来即可。(甚至不需要并查集.jpg)

    由于原图是二分图,并且所有 ([1, i-1]) 内部的连边都已经确定,我们可以直接将 ([1, i-1]) 分为两个内部没有连边的集合,套用上面的做法即可。复杂度不变,但常数略大,需要一些剪枝才能通过。

    #include <bits/stdc++.h>
    #include "chameleon.h"
    #define R register
    #define mp make_pair
    #define ll long long
    #define pii pair<int, int>
    using namespace std;
    const int mod = 998244353, N = 1100;
    
    int col[N], lv[N], vis[N], mat[N];
    vector<int> to[N];
    
    inline int addMod(int a, int b) {
    	return (a += b) >= mod ? a - mod : a;
    }
    
    inline ll quickpow(ll base, ll pw) {
    	ll ret = 1;
    	while (pw) {
    		if (pw & 1) ret = ret * base % mod;
    		base = base * base % mod, pw >>= 1;
    	}
    	return ret;
    }
    
    template <class T>
    inline void read(T &x) {
    	x = 0;
    	char ch = getchar(), w = 0;
    	while (!isdigit(ch)) w = (ch == '-'), ch = getchar();
    	while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    	x = w ? -x : x;
    	return;
    }
    
    void dfs1(int now) {
    	for (auto &v : to[now]) {
    		if (col[v] != col[now]) continue;
    		col[v] ^= 1, dfs1(v);
    	}
    	return;
    }
    
    inline void addEdg(int x, int y) {
    	if (col[x] == col[y]) col[y] ^= 1, dfs1(y);
    	to[x].push_back(y), to[y].push_back(x);
    	return;
    }
    
    vector<int> resolve(vector<int> s, int x, int flag = 0) {
    	vector<int> t;
    	if (!s.size()) return vector<int>();
    	s.push_back(x);
    	if (!flag) {
    		int w = s.size() - Query(s);
    		if (!w) return vector<int>();
    	}
    	s.pop_back();
    	if (s.size() == 1) return s;
    	int m = s.size() >> 1;
    	while (m--) t.push_back(s.back()), s.pop_back();
    	vector<int> l = resolve(t, x);
    	if (l.size() < 3) {
    		vector<int> r = resolve(s, x, l.empty());
    		while (r.size()) l.push_back(r.back()), r.pop_back();
    	}
    	return l;
    }
    
    void dfs2(int now, int rt) {
    	if (now == rt) return;
    	if (vis[now]) {
    		for (R int i = 0; i <= 2; ++i)
    			if (mat[to[now][i]] != now && lv[to[now][i]] != now)
    				lv[now] = to[now][i];
    		return dfs2(lv[now], rt);
    	}
    	vis[now] = 1;
    	vector<int> s;
    	s.push_back(now);
    	for (R int i = 0; i <= 2; ++i)
    		if (lv[to[now][i]] == now) s.push_back(to[now][i]);
    	for (R int i = 0; i <= 2; ++i)
    		if (lv[to[now][i]] != now) {
    			s.push_back(to[now][i]);
    			break;
    		}
    	if (Query(s) == 1) {
    		for (R int i = 0; i <= 2; ++i)
    			if (lv[to[now][i]] != now) {
    				mat[now] = to[now][i];
    				mat[to[now][i]] = now;
    				vis[to[now][i]] = 1;
    				Answer(now, mat[now]);
    				break;
    			}
    		for (R int i = 2; ~i; --i)
    			if (lv[to[now][i]] != now) {
    				lv[now] = to[now][i];
    				break;
    			}
    	}
    	else {
    		for (R int i = 2; ~i; --i)
    			if (lv[to[now][i]] != now) {
    				mat[now] = to[now][i];
    				mat[to[now][i]] = now;
    				vis[to[now][i]] = 1;
    				Answer(now, mat[now]);
    				break;
    			}
    		for (R int i = 0; i <= 2; ++i)
    			if (lv[to[now][i]] != now) {
    				lv[now] = to[now][i];
    				break;
    			}
    	}
    	dfs2(lv[now], rt);
    	return;
    }
    
    void Solve(int n) {
    	for (R int i = 1; i <= 2 * n; ++i) {
    		vector<int> s;
    		for (R int j = 1; j < i; ++j)
    			if (col[j]) s.push_back(j);
    		vector<int> fl = resolve(s, i);
    		s.clear();
    		for (R int j = 1; j < i; ++j)
    			if (!col[j]) s.push_back(j);
    		vector<int> fr = resolve(s, i);
    		for (auto &v : fl) addEdg(v, i);
    		for (auto &v : fr) addEdg(v, i);
    	}
    	for (R int i = 1; i <= 2 * n; ++i) {
    		if (vis[i]) continue;
    		if (to[i].size() == 1) {
    			vis[to[i][0]] = 1;
    			mat[i] = to[i][0], mat[to[i][0]] = i;
    			Answer(i, to[i][0]);
    			continue;
    		}
    		vector<int> s;
    		s.push_back(i);
    		s.push_back(to[i][0]), s.push_back(to[i][1]);
    		if (Query(s) == 1) {
    			lv[i] = to[i][2];
    			dfs2(to[i][2], i);
    			if (vis[i]) continue;
    			int v = lv[to[i][0]] == i ? to[i][1] : to[i][0];
    			Answer(i, v), vis[v] = 1, mat[i] = v, mat[v] = i;
    			continue;
    		}
    		s[2] = to[i][2];
    		if (Query(s) == 1) {
    			lv[i] = to[i][1];
    			dfs2(to[i][1], i);
    			if (vis[i]) continue;
    			int v = lv[to[i][0]] == i ? to[i][2] : to[i][0];
    			Answer(i, v), vis[v] = 1, mat[i] = v, mat[v] = i;
    			continue;
    		}
    		lv[i] = to[i][0];
    		dfs2(to[i][0], i);
    		if (vis[i]) continue;
    		int v = lv[to[i][1]] == i ? to[i][2] : to[i][1];
    		Answer(i, v), vis[v] = 1, mat[i] = v, mat[v] = i;
    	}
    	return;
    }
    
  • 相关阅读:
    二维数组最大子数组算法
    寻找最大子数组
    最大值bug 调试
    多路电梯调度算法
    分析一个文本文件各个词出现的频率,并把频率最高的十个词打印出来。
    使用redis实现生产者消费者模式
    简单使用redis实现sso单点登录
    MongoDB批量导入及简单的性能优化
    mysql安装
    字符串操作性能优化
  • 原文地址:https://www.cnblogs.com/suwakow/p/12672246.html
Copyright © 2011-2022 走看看