1. 程式人生 > 實用技巧 >題解 P5355 【[Ynoi2017]由乃的玉米田】

題解 P5355 【[Ynoi2017]由乃的玉米田】

題意簡述

見題面

題解

前三個操作就是小清新人渣的本願。

這裡簡單講解一下。

記錄兩個 bitset clainv

我們考慮莫隊。

cla[x]==1 表示 \(x\) 這個數出現過。

inv[x]==1 表示 \(100000-x\) 這個數出現過。

這兩個 bitset 的維護很簡單,就是在莫隊的加減貢獻操作裡改就行了。

對於第一個減法的判斷,我們的答案就是 ((cla<<x)&cla) 是否為 0。

如果為 0 的話表示應該輸出有解。

正確性很好得到。

比如我們的詢問是是否存在 \(a,b\) 使得 \(a-b=x\)

那麼我們只需要存在 \(a\) 以及 \(a-x\)

即可。

第二個加法的判斷也差不多,看作是加一個負數即可,判斷是 ((cla<<(100000-x)&inv))

第三個乘法的判斷直接暴力列舉因子 \(i\),判斷 \(i,\frac{x}{i}\) 是否同時存在即可。(\(i\mid x\))。

由於值域和 \(n,m\) 同階,所以我們的複雜度是對的。

對於第四個操作我們直接從乘法賀過來。

列舉一個 \(i\),從 1 開始,終止條件為 \(i\times x\le100000\)

其中 \(x\) 為當前詢問給出的商。

然後直接判斷是否同時存在 \(i\)\(i\times x\) 即可。

\(x\ge\sqrt{n}\)

的時候我們的複雜度是對的。

那麼 \(x<\sqrt{n}\) 的時候我們就換一種方法吧。

我們列舉一個 \(x\in[1,\sqrt{100000}]\)

然後維護兩個陣列 premxp

\(x\) 的列舉裡面我們再列舉一個 \(i\in[1,n]\)

然後 pre[i] 表示 \(a_{i}\) 的上一次出現位置。

mxp[i] 掃描到 \(i\) 的時候出現了滿足 \(a\div b=x\) 的最右位置。

維護的具體方法看註釋吧。

這道題就完了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <bitset>
#include <queue>

using namespace std;

const int Maxn = 1e5 + 5, Maxs = 400 + 5, Maxv = 310;
int n, m, each, cube, isa[Maxn], cnt[Maxn], ans[Maxn], pre[Maxn], mxp[Maxn], bel[Maxn], lps[Maxs], rps[Maxs];
struct Query
{
	int t, l, r, x, id;
	Query() {}
	Query(int t1, int t2, int t3, int t4, int t5)
	{
		t = t1;
		l = t2;
		r = t3;
		x = t4;
		id = t5;
	}
};
struct Special
{
	int l, r, id;
	Special() {}
	Special(int t1, int t2, int t3)
	{
		l = t1;
		r = t2;
		id = t3;
	}
};
vector < Query > Mq; // Mo's Algorithm | Query
vector < Special > Vq[Maxn]; // Violence | Query
bitset < Maxn > cla, inv; // classic | inverse

bool cmp(const Query &one, const Query &ano)
{
	if (bel[one.l] != bel[ano.l])   return one.l < ano.l;
	else if (bel[one.l] & 1)    return one.r < ano.r;
	else    return one.r > ano.r;
}

void Plus_Cont(int x)
{
	x = isa[x];
	if (cnt[x] == 0)
	{
		cla[x] = 1;
		inv[100000 - x] = 1;
	}
	++cnt[x];
}

void Mins_Cont(int x)
{
	x = isa[x];
	--cnt[x];
	if (cnt[x] == 0)
	{
		cla[x] = 0;
		inv[100000 - x] = 0;
	}
}

void Pare_v1()
{
	int l = 1, r = 0;
	for (auto it : Mq)
	{
		while (l > it.l)	Plus_Cont(--l);
		while (l < it.l)	Mins_Cont(l++);
		while (r > it.r)	Mins_Cont(r--);
		while (r < it.r)	Plus_Cont(++r);
		if (it.t == 1)  ans[it.id] = ((cla << it.x) & cla).any();
		else if (it.t == 2)  ans[it.id] = ((cla << (100000 - it.x)) & inv).any();
		else if (it.t == 3)
		{
			bool flag = 0;
			for (int i = 1; i * i <= it.x; ++i)
			{
				if (it.x % i == 0 && cla.test(i) && cla.test(it.x / i))
				{
					ans[it.id] = 1;
					flag = 1;
					break;
				}
			}
			if (flag == 0)  ans[it.id] = 0;
		}
		else
		{
			bool flag = 0;
			for (int i = 1; i * it.x <= 100000; ++i)
			{
				if (cla.test(i) && cla.test(i * it.x))
				{
					ans[it.id] = 1;
					flag = 1;
					break;
				}
			}
			if (flag == 0)	ans[it.id] = 0;
		}
	}
}

void Pare_v2()
{
	for (int x = 1; x <= Maxv; ++x)
	{
		if (Vq[x].empty())	continue;
		int now = 0;
		for (int i = 1; i <= n; ++i)
		{
			int y = isa[i];
			pre[y] = i;
			if (x * y <= 100000)	now = max(now, pre[x * y]);
			if (y % x == 0) 	now = max(now, pre[y / x]);
			mxp[i] = now;
		}
		for (auto it : Vq[x])
		{
			if (it.l <= mxp[it.r])	ans[it.id] = 1;
			else	ans[it.id] = 0; 
		}
		memset(pre, 0, sizeof pre);
		memset(mxp, 0, sizeof mxp);
	}
}

char ANS[2][10] = { "yumi", "yuno" };
signed main()
{
	scanf("%d %d", &n, &m);
	each = 320;
	cube = (n - 1) / each + 1;
	for (int i = 1; i <= n; ++i)	scanf("%d", &isa[i]);
	for (int i = 1; i <= cube; ++i)
	{
		lps[i] = rps[i - 1] + 1;
		rps[i] = rps[i - 1] + each;
		if (i == cube) 	rps[i] = n;
		for (int j = lps[i]; j <= rps[i]; ++j)  bel[j] = i;
	}
	for (int i = 1, t, l, r, x; i <= m; ++i)
	{
		scanf("%d %d %d %d", &t, &l, &r, &x);
		if (t == 4 && x <= Maxv)    Vq[x].emplace_back(Special(l, r, i));
		else    Mq.emplace_back(Query(t, l, r, x, i));
	}
	sort(Mq.begin(), Mq.end(), cmp);
	Pare_v1(), Pare_v2();
	for (int i = 1; i <= m; ++i)    puts(ANS[ans[i]]);
	return 0;
}