「Ynoi2015」盼君勿忘

这个题是刚考挂 CSP 回来写的。

Idea:lxl Solution:lxl Std:lxl Data:lxl

对这题的评价:4/11

[Luogu 5072]

题意简述

一个长为 $n$ 的整数序列, $m$ 次查询。每次给出 $l, r, p$ ,查询区间 $[l,r]$ 中所有子序列分别去重(即一个子序列中出现多次的数只留下一个)后的和 $\bmod\ p$ 。

$1\le n, m, a_i\le 10^5, 1\le p\le 10^9$ ,不强制在线

主要思路

一个长度为 $len$ 的询问,如果一个数 $x$ 出现了 $k$ 次,对答案的贡献是 $x(2^{len - k}(2^k - 1))$ 。

$2^{len - k}$ 是除了 $x$ 以外的数的子序列个数, $2^k - 1$ 是所有 $x$ 构成的子序列中包含 $x$ 的种数。

把出现个数为 $i$ 的数的和记为 $s_i$ ,则只需要维护 $s$ 即可。

肯定是离线下来莫队,但是模数不同不好求答案。

发现出现个数大于 $\sqrt{n}$ 的数的个数不会超过 $\sqrt{n}$ ,所以将出现次数大于 $\sqrt{n}$ 的数用一个东西来维护,复杂度就是单次 $O(\sqrt{n})$ 的了。

然后快速幂要用那种 $O(\sqrt{n})$ 预处理 $O(1)$ 求幂次的方法(就是处理 $2^0, 2^1, 2^2, \dots, 2^{\sqrt{n}}$ 和 $2^{\sqrt{n}}, 2^{2\sqrt{n}}, \dots, 2^{n}$ )。

出现次数大于 $\sqrt{n}$ 的数可以用哈希表来维护。 unordered_set 居然没被卡。

于是就时间复杂度 $O((n + m)\sqrt{n})$ ,空间复杂度 $O(n)$ 了。

参考代码

然后不知道为什么跑得巨慢,花了 std 的两倍时间。
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define reg register
#define Rint register int
#define FOR(i,a,b) for(register int i=(a),ed_##i=(b);i<=ed_##i;++i)
#define ROF(i,a,b) for(register int i=(a),ed_##i=(b);i>=ed_##i;--i)
#define FORit(templ,arr,i,a,b) for(register templ *i=(arr)+(a),*ed_##i=(arr)+(b)+1;i!=ed_##i;++i)
#define ROFit(templ,arr,i,a,b) for(register templ *i=(arr)+(a),*ed_##i=(arr)+(b)-1;i!=ed_##i;--i)
#define GO(x,p,e,i,v) for(register int i=p[x].head,v;i;i=e[i].link)
#define MEM(x,v) memset(x,v,sizeof(x))
#define fir first
#define sec second
#define pq priority_queue
#define MP make_pair
typedef long long LL;
typedef double DB;
typedef pair<int,int> PII;
#define Templ(T) template<typename T>
inline int read(){
int ans=0,f=1;char c=getchar();
while(!isdigit(c)){ f^=(c=='-'); c=getchar(); }
for(;isdigit(c);c=getchar()) ans=(ans<<1)+(ans<<3)+(c^48); return f?ans:-ans;
}
Templ(_Tp) inline int chkmin(_Tp &x,_Tp y){ return x>y?x=y,1:0; }
Templ(_Tp) inline int chkmax(_Tp &x,_Tp y){ return x<y?x=y,1:0; }
// #define using_mod
const int N = 100010, BN = 330;
#ifdef using_mod
inline void inc(int &x,const int &y){ x+=y; if(x>=mod) x-=mod; }
inline int ksm(int x,LL y){ int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod; return res;}
#endif
Templ(_Tp) inline _Tp gcd(_Tp x,_Tp y){ return y?gcd(y,x%y):x; }
#define FILE(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define PBTXDY
}
using namespace my_std;

const int blo = 317;
int n, m, a[N], ans[N], bl[N], cnt[N];
struct Query{
int l, r, mod, id;
inline int operator <(const Query &t)const{
return bl[l] == bl[t.l] ? (bl[l] & 1 ? r < t.r : r > t.r) : l < t.l ;
}
}Que[N];

LL sum[BN];
unordered_set<LL>s;

int p_2[BN], pb2[N];

inline int ksm2(const int &x, const int &mod){ return 1ll * p_2[x % blo] * pb2[x / blo] % mod; }
inline void inc(int &x, const int &y, const int &mod){ x += y; if(x >= mod) x -= mod; }

inline void Query(int id){
const int mod = Que[id].mod;
Rint l = Que[id].l, r = Que[id].r, res = 0, len = r - l + 1;
FOR(i, 1, blo) p_2[i] = 2ll * p_2[i - 1] % mod;
pb2[1] = p_2[blo];
Rint tmp = pb2[1];
FOR(i, 2, blo) pb2[i] = 1ll * tmp * pb2[i - 1] % mod;
tmp = ksm2(len, mod);
FOR(j, 1, blo) inc(res, sum[j] * (tmp - ksm2(len - j, mod) + mod) % mod, mod);
for(int t : s) inc(res, 1ll * t * (tmp - ksm2(len - cnt[t], mod) + mod) % mod, mod);
ans[Que[id].id] = res;
}

inline void update_add(int x){
if(cnt[x] > blo) cnt[x]++;
else{
sum[cnt[x]++] -= x;
if(cnt[x] > blo) s.insert(x);
else sum[cnt[x]] += x;
}
}

inline void update_del(int x){
if(cnt[x] > blo + 1) cnt[x]--;
else{
if(cnt[x] > blo) s.erase(x), sum[--cnt[x]] += x;
else sum[cnt[x]--] -= x, sum[cnt[x]] += x;
}
}

int main(){
n = read(), m = read();
// blo = ceil(sqrt(n));
p_2[0] = pb2[0] = 1;
Rint cb = 1;
FOR(i, 1, n){
a[i] = read();
bl[i] = (i % blo) ? cb : cb++;
}
FOR(i, 1, m) Que[i].id = i, Que[i].l = read(), Que[i].r = read(), Que[i].mod = read();
Rint l = 1, r = 0;
sort(Que + 1, Que + m + 1);
FOR(i, 1, m){
while(r < Que[i].r) update_add(a[++r]);
while(l > Que[i].l) update_add(a[--l]);
while(r > Que[i].r) update_del(a[r--]);
while(l < Que[i].l) update_del(a[l++]);
Query(i);
}
FOR(i, 1, n) printf("%d\n", ans[i]);
return 0;
}