Codeforces Round 974 (Div. 3) H题 Robin Hood Archery(基础莫队,随机异或哈希)

题目链接

Codeforces Round 974 (Div. 3) H题 Robin Hood Archery

思路1

因为警长是后手,按照最优的策略,只有每一种数的个数是偶数个的时候,警长会平局,否则警长会输。

随着询问区间端点的变化,答案的转移是 O ( 1 ) O(1) O(1)的。因此,我们可以使用基础莫队进行离线求解。

代码1

#pragma GCC optimize("O2")
#pragma GCC optimize("O3")
#include 
using namespace std;
#define int long long
#define double long double
#define debug() cout<<"-------------------"<<endl;
typedef long long i64;
typedef pair<int, int> pii;
const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
int n, m;
int a[N], ans[N];
int cnt[M];//桶
struct MOA
{
	struct query
	{
		int id, l, r;
	};

	int len;
	vector<query>q;

	MOA() {}
	MOA(int n, int m) {init(n, m);}

	void init(int n, int m)
	{
		len = sqrt(n);
		q.resize(m + 1);
	}

	void addquery(int i, int l, int r)
	{
		q[i] = {i, l, r};
	}

	int get(int x)
	{	//求每个编号对应块
		return x / len;
	}

	void add(int x, int &res)
	{
		if (cnt[x] % 2 == 0)
			res++;
		else res--;
		cnt[x]++;
	}

	void del(int x, int &res)
	{
		if (cnt[x] & 1)
			res--;
		else res++;
		cnt[x]--;
	}

};
void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		cnt[a[i]] = 0;
	}
	MOA moa(n, m);
	for (int i = 1; i <= m; i++)
	{
		int l, r;
		cin >> l >> r;
		moa.addquery(i, l, r);
	}

	sort(moa.q.begin() + 1, moa.q.begin() + 1 + m, [&moa](MOA::query a, MOA::query b) {
		int i = moa.get(a.l);
		int j = moa.get(b.l);
		if (i != j) return i < j;
		return a.r < b.r;
	});

	int i = 0;//向r靠齐的指针
	int j = 1;//向l靠齐的指针
	int res = 0;

	for (int k = 1; k <= m; k++)
	{
		int id = moa.q[k].id;
		int l = moa.q[k].l;
		int r = moa.q[k].r;

		while (i < r)
		{
			moa.add(a[++i], res);
		}
		while (i > r)
		{
			moa.del(a[i--], res);
		}
		while (j < l)
		{
			moa.del(a[j++], res);
		}
		while (j > l)
		{
			moa.add(a[--j], res);
		}
		ans[id] = res;
	}
	for (int i = 1; i <= m; i++)
	{
		if (ans[i])
		{
			cout << "NO" << endl;
		}
		else cout << "YES" << endl;
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int test = 1;
	cin >> test;
	for (int i = 1; i <= test; i++)
	{
		solve();
	}
	return 0;
}

思路2

对于 [ a l , a r ] [a_{l},a_{r}] [al,ar]是否可以两两抵消,我们可以通过区间异或值是否为 0 0 0进行判断。

但这样随便一个hack都可以死(比如1,2,4,7)。

但我们可以进行随机赋值,把 [ 1 , 1 e 6 ] [1,1e6] [1,1e6]随机映射到一个极大的范围,之后使用异或哈希即可。

代码2

#pragma GCC optimize("O2")
#pragma GCC optimize("O3")
#include 
using namespace std;
#define int long long
#define double long double
#define debug() cout<<"-------------------"<<endl;
typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;
const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
std::mt19937 rnd(time(0));
int n, m;
int a[N];
u64 s[N], v[M];
void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		s[i] = s[i - 1] ^ v[a[i]];
	}

	while (m--)
	{
		int l, r;
		cin >> l >> r;
		if ((r - l + 1) & 1)
		{
			cout << "NO" << endl;
		}
		else
		{
			if ((s[r] ^ s[l - 1]) == 0)
			{
				cout << "YES" << endl;
			}
			else cout << "NO" << endl;
		}
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	for (int i = 1; i <= 1e6; i++)
	{
		v[i] = rnd();
	}
	int test = 1;
	cin >> test;
	for (int i = 1; i <= test; i++)
	{
		solve();
	}
	return 0;
}

你可能感兴趣的:(Codeforces,哈希算法,散列表,算法,c++,数据结构)