其他分享
首页 > 其他分享> > 【题解】P5072 [Ynoi2015] 盼君勿忘

【题解】P5072 [Ynoi2015] 盼君勿忘

作者:互联网

题意

P5072 [Ynoi2015] 盼君勿忘

给定一个长度为 \(n\) 的序列 \(a\) 和 \(m\) 个询问 \(l, r, p\),每次询问 \([l, r]\) 中所有子序列去重后的和 \(\bmod p\)

\(1 \leq n, m, a_i \leq 10^5, 1 \leq p \leq 10^9, 1 \leq l \leq r \leq n\)

思路

莫队 + 光速幂。

操作不带修,考虑莫队。

考虑每个值对答案的贡献。

设当前区间为 \([l, r]\),值 \(v\) 在 \([l, r]\) 内的出现次数为 \(cnt_v\)

很难直接求去重后包含值 \(k\) 的子序列个数,不妨稍加转化。如果将这些子序列中的 \(k\) 删除,则这些子序列中一定不出现 \(k\)。容易看出 \([l, r]\) 内不包含 \(k\) 的子序列个数共有 \(2^{r - l + 1 - cnt_k}\) 个。这意味着包含值 \(k\) 的子序列共有 \(2^{r - l + 1} - 2^{r - l + 1 - cnt_k}\) 个。出现次数直接用普通莫队维护即可,复杂度是 \(\mathcal{O}(n \sqrt{q})\)

对于出现次数相同的值,我们可以将它们的贡献一起统计。这里的实现可以维护一个针对出现次数的值域双向链表(?),每次从当前的出现次数开始向下一个存在的出现次数跳。双向链表的插入和删除可以 \(\mathcal{O}(1)\) 做。显然询问的复杂度和值出现次数的个数有关,又易知对于不同的值出现次数,其个数是 \(\mathcal{O}(\sqrt{n})\) 的。所以这一部分的复杂度是 \(\mathcal{O}(q \sqrt{n})\)

普通的快速幂会被卡常。求 \(2\) 的整次幂可以用光速幂,这里不再赘述。这里光速幂单次预处理的复杂度是 \(\mathcal{O}(\sqrt{n})\),查询 \(\mathcal{O}(1)\)

总复杂度 \(\mathcal{O}(n \sqrt{q} + q \sqrt{n})\)

代码

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

typedef long long ll;

const int maxn = 1e5 + 5;
const int max_block = 1e5 + 5;

int n, m;
int tot, bl;
int pre[maxn], nxt[maxn];
int a[maxn], bel[maxn], cnt[maxn];
ll f[max_block + 10][2];
ll sum[maxn];
ll ans[maxn];

struct ques
{
	int l, r, p, id;

	bool operator < (const ques &rhs) const
	{
		if (bel[l] ^ bel[rhs.l]) return bel[l] < bel[rhs.l];
		return (bel[l] & 1 ? r < rhs.r : r > rhs.r);
	}
} q[maxn];

void insert(int k)
{
	nxt[tot] = k;
	pre[k] = tot;
	tot = k;
}
	
void erase(int k)
{
	if (tot == k)
	{
		nxt[pre[k]] = 0;
		tot = pre[k];
	}
	else
	{
		nxt[pre[k]] = nxt[k];
		pre[nxt[k]] = pre[k];
	}
	pre[k] = nxt[k] = 0;
}

inline void init(int mod)
{
	f[0][0] = 1;
	for (int i = 1; i <= bl; i++) f[i][0] = 2ll * f[i - 1][0] % mod;
	f[0][1] = 1;
	for (int i = 1; i <= bl; i++) f[i][1] = 1ll * f[i - 1][1] * f[bl][0] % mod;
}

inline ll query(int power, int mod)
{
	return f[power % bl][0] * f[power / bl][1] % mod;
}

inline void add(int pos)
{
	sum[cnt[a[pos]]] -= a[pos];
	if (!sum[cnt[a[pos]]]) erase(cnt[a[pos]]);
	cnt[a[pos]]++;
	if (!sum[cnt[a[pos]]]) insert(cnt[a[pos]]);
	sum[cnt[a[pos]]] += a[pos];
}

inline void del(int pos)
{
	sum[cnt[a[pos]]] -= a[pos];
	if (!sum[cnt[a[pos]]]) erase(cnt[a[pos]]);
	cnt[a[pos]]--;
	if (!sum[cnt[a[pos]]]) insert(cnt[a[pos]]);
	sum[cnt[a[pos]]] += a[pos];
}

int main()
{
	int l = 1, r = 0;
	scanf("%d%d", &n, &m);
	int block = n / sqrt(m);
	bl = block + 1;
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &a[i]);
		bel[i] = (i - 1) / block + 1;
	}
	for (int i = 1; i <= m; i++)
	{
		scanf("%d%d%d", &q[i].l, &q[i].r, &q[i].p);
		q[i].id = i;
	}
	sort(q + 1, q + m + 1);
	for (int i = 1; i <= m; i++)
	{
		while (r < q[i].r) add(++r);
		while (l > q[i].l) add(--l);
		while (l < q[i].l) del(l++);
		while (r > q[i].r) del(r--);
		int len = r - l + 1;
		init(q[i].p);
		for (int j = nxt[0]; j; j = nxt[j])
		{
			ll val = sum[j] * (query(len, q[i].p) - query(len - j, q[i].p) + q[i].p) % q[i].p;
			ans[q[i].id] = (ans[q[i].id] + val) % q[i].p;
		}
	}
	for (int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
	return 0;
}

标签:pre,nxt,int,题解,盼君,Ynoi2015,leq,maxn,mathcal
来源: https://www.cnblogs.com/lingspace/p/p5072.html