ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-03-18 21:36:45  阅读:154  来源: 互联网

标签:pre nxt int 题解 盼君 Ynoi2015 leq maxn mathcal


题意

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有