做的第一道莫队题 QwQ。
给定一个序列,每次查询一个区间 [ l , r ] [l,r] [l,r] 中所有子序列分别去重后的和 m o d p \bmod\ p mod p。
思路
莫队 + 手写双向链表
1
题意有点绕,但注意是“分别去重”。
那么假设一个数 x x x,它在长为 n n n 的序列中出现了 m m m 次,即一共有 2 n 2^n 2n 个子序列,不含 x x x 的子序列有 2 n − m 2^{n-m} 2n−m 个。
则 x x x 在所有去重后的子序列中出现了 ( 2 n − 2 n − m ) (2^n-2^{n-m}) (2n−2n−m) 次,那么 x x x 对答案的贡献为 x ∗ ( 2 n − 2 n − m ) x * (2^n-2^{n-m}) x∗(2n−2n−m)。
2
所以问题转化为记录在区间 [ l , r ] [l,r] [l,r] 中 x x x 一共出现了几次。
莫队。
同时,因为要维护出现次数为 m m m 的数,所以还要手写一个双向链表(不手写会 TLE)。然后就是莫队板子了。
3
如果用快速幂去计算 2 的若干次方的话,由于 n n n 和 m m m 很大,就容易超时。
所以要使用光速幂。光速幂在计算固定底数、指数很大的幂时有很高的效率。
预处理 O(n) \text{O(n)} O(n),计算该底数的 1 , 2 , 3 , ⋯ , n 1,2,3,\cdots ,\sqrt{n} 1,2,3,⋯,n 次幂和 2 n , 3 , n , ⋯ , n n 2\sqrt{n},\ 3,\sqrt{n},\ \cdots ,n\sqrt{n} 2n, 3,n, ⋯,nn 次幂。
查询 O(1) \text{O(1)} O(1), 2 x 2^x 2x 即为 p o w 1 ( x % l e n ) ∗ p o w 2 ( ⌊ x l e n ⌋ ) ( l e n = ( n ) ) pow1(x \% len) * pow2(\left\lfloor\frac{x}{len}\right\rfloor)\ (len=\sqrt(n)) pow1(x%len)∗pow2(⌊lenx⌋) (len=(n))。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 100005
int n, m, len, tot;
int a[maxn], cnt[maxn], sum[maxn], ans[maxn];
int pw1[maxn], pw2[maxn];
inline int get(int x)
{
return (x - 1) / len + 1;
}
struct relist{
struct node2{
int pre, nxt;
}d[maxn];
int tot;
}lst;
struct node{
int l, r, p, id;
friend bool operator < (const node &a, const node &b)
{
return get(a.l) == get(b.l) ? a.r < b.r : get(a.l) < get(b.l);
}
}q[maxn];
inline void power_pre(int mod)
{
pw1[0] = pw2[0] = 1;
for(int i = 1; i <= len + 3; ++i) pw1[i] = pw1[i - 1] * 2 % mod;
for(int i = 1; i <= len + 3; ++i) pw2[i] = pw2[i - 1] * pw1[len] % mod;
}
inline int qry(int k, int mod)
{
return pw1[k % len] % mod * pw2[k / len] % mod;
}
inline void era(int x)
{
if(x != tot)
{
lst.d[lst.d[x].nxt].pre = lst.d[x].pre;
lst.d[lst.d[x].pre].nxt = lst.d[x].nxt;
}
else
{
lst.d[lst.d[x].pre].nxt = 0;
tot = lst.d[x].pre;
}
lst.d[x].pre = lst.d[x].nxt = 0;
}
inline void add_back(int x)
{
lst.d[tot].nxt = x, lst.d[x].pre = tot;
tot = x;
}
inline void updt(int x, int tim)
{
if(!(sum[cnt[a[x]]] -= a[x]))
era(cnt[a[x]]);
if(!sum[cnt[a[x]] += tim])
add_back(cnt[a[x]]);
sum[cnt[a[x]]] += a[x];
}
signed main()
{
scanf("%lld %lld", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
len = ceil(sqrt(n));
for(int i = 1; i <= m; ++i)
{
scanf("%lld %lld %lld", &q[i].l, &q[i].r, &q[i].p);
q[i].id = i;
}
sort(q + 1, q + m + 1);
for(int i = 1, l = 1, r = 0; i <= m; ++i)
{
power_pre(q[i].p);
while(l > q[i].l) l -= 1, updt(l, 1);
while(r < q[i].r) r += 1, updt(r, 1);
while(l < q[i].l) updt(l, -1), l += 1;
while(r > q[i].r) updt(r, -1), r -= 1;//注意这四个的顺序不能改变
for(int j = lst.d[0].nxt; j; j = lst.d[j].nxt)
{
int totl = qry(r - l + 1, q[i].p);
int disc = qry(r - l + 1 - j, q[i].p);
int res = ((totl - disc) * sum[j] + q[i].p) % q[i].p;
ans[q[i].id] = (ans[q[i].id] + res + q[i].p) % q[i].p;
}
}
for(int i = 1; i <= m; ++i) printf("%lld\n", ans[i]);
return 0;
}
—— E n d End End——