题目传送门
Ynoi为数不多的可做的题(因为一个垃圾错误,调了一天,醉了)。
在此,orz wfy。
Solution
lxl的题,观察题面可以发现,没有修改操作,再审视题面,我们可以得到每一个相同数对区间的贡献:
$$
\begin{aligned}
ans += (2^{length} - 2^{length - k}) * a[i]
\end{aligned}
$$
区间总长度为$length$,$a[i]$在区间中出现的次数为$k$次。
证明:考虑简单容斥,总的方案数为$2^{length}$,不取$a[i]$的情况共$2^{length-k}$种,由于子序列已经去重,所以$a[i]$的贡献就为:$(2^{length} - 2^{length - k}) * a[i]$。
想到这里,不难看出这道题较为适合用莫队解决,但是由于区间长度在改变,修改操作并不是很好进行,我们考虑将每个数的出现次数分块,总区间中出现次数$> \sqrt n$的数单独统计次数(其数量一定小于$\sqrt n$个),而出现次数相同时,上式显然满足乘法结合律,统计出现次数为$cnt$的数的值的和,可以发现只需枚举$cnt$即可,而$cnt$显然小于等于$\sqrt n$即可。
此时的时间复杂度已经降为$O(n \sqrt n \log n)$,可以拿到暴力分:18分。
想办法优化掉log,我们看出所有的快速幂的底数都为2,直接套光速幂,预处理出$2^{k \sqrt n}$和$2^k$的值,计算是直接取即可。
时间复杂度: $O((m + n) \sqrt n)$。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| #include<bits/stdc++.h> #define LL long long using namespace std; const int N = 1e5 + 5; int n, m; int pos[N]; struct node{ int l, r; int id, mod; bool operator< (node b) const{ if(pos[l] == pos[b.l]) return r < b.r; return l < b.l; } }q[N]; LL a[N]; int len, limit, cnt, tot[N], num[N]; LL seg[N]; vector<int> s; inline void add(int x) { cnt ++; if(tot[a[x]] > limit) return num[a[x]] ++, void(); seg[num[a[x]]] -= a[x]; num[a[x]] ++; seg[num[a[x]]] += a[x]; } inline void del(int x) { cnt --; if(tot[a[x]] > limit) return num[a[x]] --, void(); seg[num[a[x]]] -= a[x]; num[a[x]] --; seg[num[a[x]]] += a[x]; } LL ans[N]; LL maxn = 0; LL sum[N], val[N]; inline void init(int mod) { val[0] = sum[0] = 1; for(int i = 1;i <= limit;i ++) val[i] = (val[i - 1] + val[i - 1]) % mod; sum[1] = val[limit]; for(int i = 2;i <= limit;i ++) sum[i] = sum[i - 1] * sum[1] % mod; } inline LL make(int x, int mod) { return sum[x / limit] * val[x % limit] % mod; } int main() { scanf("%d%d", &n, &m); for(int i = 1;i <= n;i ++) scanf("%lld", &a[i]); for(int i = 1;i <= n;i ++) tot[a[i]] ++; for(int i = 1;i <= m;i ++) scanf("%d%d%d", &q[i].l, &q[i].r, &q[i].mod); for(int i = 1;i <= m;i ++) q[i].id = i; limit = sqrt(n) + 1; for(int i = 1;i <= 1e5;i ++) if(tot[i] > limit) s.push_back(i); for(int i = 1;i <= n;i ++) { if(i > limit * len) len ++; pos[i] = len; } sort(q + 1, q + m + 1); for(int i = 1, l = 1, r = 0; i <= m;i ++) { int mod = q[i].mod; init(mod); while(l > q[i].l) add(-- l); while(r < q[i].r) add(++ r); while(l < q[i].l) del(l ++); while(r > q[i].r) del(r --); maxn = make(cnt, mod); for(int j = 1; j <= limit; j ++) ans[q[i].id] += seg[j] * (maxn - make(cnt - j, mod) + mod) % mod; for(int j = 0; j < (int)s.size(); j ++) if(num[s[j]] != 0) ans[q[i].id] += s[j] * (maxn - make(cnt - num[s[j]], mod) + mod) % mod; ans[q[i].id] = ans[q[i].id] % q[i].mod; } for(int i = 1;i <= m;i ++) printf("%lld\n", ans[i]); return 0; }
|