题意
P5072 [Ynoi2015] 盼君勿忘
给定一个长度为 n n 的序列 a a 和 m m 个询问 l , r , p l , r , p ,每次询问 [ l , r ] [ l , r ] 中所有子序列去重后的和 mod p mod p
1 ≤ n , m , a i ≤ 10 5 , 1 ≤ p ≤ 10 9 , 1 ≤ l ≤ r ≤ n 1 ≤ n , m , a i ≤ 10 5 , 1 ≤ p ≤ 10 9 , 1 ≤ l ≤ r ≤ n
思路
莫队 + 光速幂。
操作不带修,考虑莫队。
考虑每个值对答案的贡献。
设当前区间为 [ l , r ] [ l , r ] ,值 v v 在 [ l , r ] [ l , r ] 内的出现次数为 c n t v c n t v
很难直接求去重后包含值 k k 的子序列个数,不妨稍加转化。如果将这些子序列中的 k k 删除,则这些子序列中一定不出现 k k 。容易看出 [ l , r ] [ l , r ] 内不包含 k k 的子序列个数共有 2 r − l + 1 − c n t k 2 r − l + 1 − c n t k 个。这意味着包含值 k k 的子序列共有 2 r − l + 1 − 2 r − l + 1 − c n t k 2 r − l + 1 − 2 r − l + 1 − c n t k 个。出现次数直接用普通莫队维护即可,复杂度是 O ( n √ q ) O ( n q )
对于出现次数相同的值,我们可以将它们的贡献一起统计。这里的实现可以维护一个针对出现次数的值域双向链表(?),每次从当前的出现次数开始向下一个存在的出现次数跳。双向链表的插入和删除可以 O ( 1 ) O ( 1 ) 做。显然询问的复杂度和值出现次数的个数有关,又易知对于不同的值出现次数,其个数是 O ( √ n ) O ( n ) 的。所以这一部分的复杂度是 O ( q √ n ) O ( q n )
普通的快速幂会被卡常。求 2 2 的整次幂可以用光速幂,这里不再赘述。这里光速幂单次预处理的复杂度是 O ( √ n ) O ( n ) ,查询 O ( 1 ) O ( 1 )
总复杂度 O ( n √ q + q √ n ) O ( n q + q n )
代码
view code #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 ;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话