题意:
读入一个长度为
n
n
n的整数数列
a
1
,
a
2
,
…
,
a
n
a_1,a_2,…,a_n
a1,a2,…,an,以及一个整数
K
K
K。
q
q
q组询问。
每组询问包含一个二元组
(
l
,
r
)
(l, r)
(l,r), 其中
1
≤
l
≤
r
≤
n
1≤l≤r≤ n
1≤l≤r≤n,
求所有满足以下条件的二元组
(
l
2
,
r
2
)
(l_2, r_2)
(l2,r2)的数目:
1:
1
≤
l
≤
l
2
≤
r
2
≤
r
≤
n
,
1≤l≤l_2≤r_2≤r≤n,
1≤l≤l2≤r2≤r≤n,
2:
∑
i
=
l
2
r
2
a
[
i
]
\sum_{i=l_2}^{r_2}a[i]
∑i=l2r2a[i]是
K
K
K的倍数。
思路:
区间和首先尝试用前缀和进行转化
则对于任意的一个询问区间
[
l
,
r
]
[l,r]
[l,r],满足条件的子区间个数为:
A
n
s
=
∑
l
2
=
l
r
∑
r
2
=
l
2
r
[
(
s
u
m
[
r
2
]
−
s
u
m
[
l
2
−
1
]
)
%
k
=
=
0
]
Ans = \sum_{l_2 = l}^{r} \sum_{r_2 = l_2}^{r} [(sum[r_2] - sum[l_2-1]) \% k == 0]
Ans=l2=l∑rr2=l2∑r[(sum[r2]−sum[l2−1])%k==0]
若对前缀和数组遍历对
k
k
k取余,则:
A
n
s
=
∑
l
2
=
l
r
∑
r
2
=
l
2
r
[
s
u
m
[
r
2
]
=
=
s
u
m
[
l
2
−
1
]
]
Ans = \sum_{l_2 = l}^{r} \sum_{r_2 = l_2}^{r} [sum[r_2] == sum[l_2-1]]
Ans=l2=l∑rr2=l2∑r[sum[r2]==sum[l2−1]]
故此时询问可以转化为求区间 [ l − 1 , r ] [l-1,r] [l−1,r]中取两个相同数的对数。
此时便是经典的莫队问题,可参考BZOJ 2038
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
const int A = 1e5 + 10;
ll sum[A],cnt[A],Ans[A],res,k;
int len,n,q;
class Que{
public:
int l,r,id;
bool operator<(const Que& rhs)const{
if(l/len == rhs.l/len) return r/len < rhs.r/len;
return l/len < rhs.l/len;
}
}Q[A];
vector<ll> v;
void update(int pos,int v){
if(v){
res += cnt[sum[pos]];
cnt[sum[pos]]++;
}
else{
cnt[sum[pos]]--;
res -= cnt[sum[pos]];
}
}
void solve(){
res = 0;
memset(cnt,0,sizeof(cnt));
int L = 0,R = -1;
for(int i=1 ;i<=q ;i++){
int id = Q[i].id;
while(R < Q[i].r) update(++R,1);
while(L > Q[i].l) update(--L,1);
while(R > Q[i].r) update(R--,0);
while(L < Q[i].l) update(L++,0);
Ans[id] = res;
}
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d%lld",&n,&q,&k);
len = sqrt(1.0*n);
sum[0] = 0;v.push_back(0);
for(int i=1 ;i<=n ;i++){
ll x;scanf("%lld",&x);
sum[i] = (sum[i-1] + x) % k;
v.push_back(sum[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=0 ;i<=n ;i++) sum[i] = lower_bound(v.begin(),v.end(),sum[i]) - v.begin();
for(int i=1 ;i<=q ;i++){
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id = i;Q[i].l--;
}
sort(Q+1,Q+1+q);
solve();
for(int i=1 ;i<=q ;i++){
printf("%lld\n",Ans[i]);
}
v.clear();
}
return 0;
}