CF761 Div2 D2. Too Many Impostors题解

Problem - D2 - Codeforces

首先发现能找到1好人1坏人就可以用这两个人确定所有人好坏。

考虑如何找,发现题目给的条件满足鸽笼原理,即我们三个人三个人划分为一组,那么一定会有一组好人多于坏人,一组坏人多于好人,不妨分别记为good组、bad组(此时花费n/3步)

考虑从bad组拿出2个人固定,去查good组的3个人,分几种情况讨论一下:(花费3步)

1)存在一次好人更多,则说明bad组剩下一个一定是坏人,且good组查出来了一个好人,找到一好一坏。

2)不存在坏人更多,那么当前bad组两个人都是坏人,剩下一个不确定,我们拿出bad组中已经确定是坏人的一个,然后查good组的任两个人:此时若坏人更多,good组剩下一个一定好人,否则当前两个都是好人。故一定找到一好一坏(花费1步)

此时我们剩下(n * 2 / 3 + 2)步,貌似确定所有人不够?但是在第一步中,我们已经知道了每个组3个人中好人多还是坏人多,因此确定一个组3个人只需要任查组内两个人即可,满足步数限制。

//#define LOCAL
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define sc second
#define pb push_back
#define ll long long
#define trav(v,x) for(auto v:x)
#define all(x) (x).begin(), (x).end()
#define VI vector<int>
#define VLL vector<ll>
#define pll pair<ll, ll>
#define double long double
//#define int long long
using namespace std;
const int N = 1e6 + 100;
const int inf = 1e9;
//const ll inf = 1e18
const ll mod = 998244353;//1e9 + 7

#ifdef LOCAL
void debug_out(){cerr << endl;}
template<typename Head, typename... Tail>
void debug_out(Head H, Tail... T)
{
	cerr << " " << to_string(H);
	debug_out(T...);
}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif


int n;

int sta[N],  id[N][3], ans[N][3], res[N];

int chk(int x, int y, int z)
{
	cout << '?' << ' ' << x << ' ' << y << ' ' << z << endl;
	int res;
	cin >> res;
	return res;
}

void out()
{
	cout << '!' << ' ' ;
	int num = 0;
	for(int i = 1; i <= n * 3; i++)
		if(res[i] == 0)++num;
	cout << num << ' ' ;
	for(int i = 1; i <= n * 3; i++)
		if(res[i] == 0)cout << i << ' ';
	cout << endl;
}

void sol()
{
	cin >> n;
	n /= 3;
	for(int i = 1; i <= 3 * n; i += 3)
	{
		sta[(i + 2) / 3] = chk(i, i + 1, i + 2);
		id[(i + 2) / 3][0] = i;
		id[(i + 2) / 3][1] = i + 1;
		id[(i + 2) / 3][2] = i + 2;
	}
	int bad = 0, good = 0;
	for(int i = 1; i <= n; i++)
	{
		if(sta[i] == 0 && !bad)
			bad = i;
		if(sta[i] == 1 && !good)
			good = i;
	}
	{
		bool flg = 0;
		int y;
		for(int i = 0; i <= 2; i++)
		{
			int nw = chk(id[bad][0], id[bad][1], id[good][i]);
			if(nw == 1)
			{
				flg = 1;
				y = i;
				break;
			}
		}
		if(flg)
		{
			swap(id[bad][0], id[bad][2]);
			swap(id[bad][1], id[good][y]);
		}
		else
		{
			int nw = chk(id[bad][0], id[good][0], id[good][1]);
			if(nw == 1)
			{
				swap(id[bad][1], id[good][0]);
			}
			else swap(id[bad][1], id[good][2]);
		}
		for(int i = 0; i < 3; i++)
			swap(id[1][i], id[bad][i]);
		sta[bad] = chk(id[bad][0], id[bad][1], id[bad][2]);
		sta[good] = chk(id[good][0], id[good][1], id[good][2]);
		ans[1][0] = 0, ans[1][1] = 1;
		ans[1][2] = chk(id[1][0], id[1][1], id[1][2]);
	}
	for(int i = 2; i <= n; i++)
	{
		if(sta[i] == 0)
		{
			int nw = chk(id[1][1], id[i][0], id[i][1]);
			if(nw == 0)
			{
				ans[i][0] = ans[i][1] = 0;
				ans[i][2] = chk(id[1][0], id[1][1], id[i][2]);
			}
			else
			{
				ans[i][2] = 0;
				ans[i][0] = chk(id[1][0], id[1][1], id[i][0]);
				ans[i][1] = (1 ^ ans[i][0]);
			}
		}
		else
		{
			int nw = chk(id[1][0], id[i][0], id[i][1]);
			if(nw == 1)
			{
				ans[i][0] = ans[i][1] = 1;
				ans[i][2] = chk(id[1][0], id[1][1], id[i][2]);
			}
			else
			{
				ans[i][2] = 1;
				ans[i][0] = chk(id[1][0], id[1][1], id[i][0]);
				ans[i][1] = (1 ^ ans[i][0]);
			}
		}
	}
	//output
	for(int i = 1; i <= n; i++)
		for(int j = 0; j <= 2; j++)
			res[id[i][j]] = ans[i][j];
	out();
}

signed main()
{
	int tt;
	cin >> tt;
	while(tt--)
		sol();
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值