T1:水题
T2:留坑
T3:
light
题意:给定长度为n(<=100000)的序列,多次(<=100000)询问,在区间[l,r]中,mod p(1<=1e9)等于v的数有多少个?
其中,序列中每个数的大小<=10000
题解:
p>1e4没有用的。
直接对p处理不好处理。
考虑分块
这里是对p分块,而不是对序列分块(不熟悉,没想到对p分块)!!
sqrt(10000)=100
p<=100时,v<=100
那么,我们可以把询问离线,利用前缀和拆分成两个点,记录前缀和再相减。
然后,我们枚举p,i从1到n扫过去。num[j]记录当前mod p = j的数多少个。
到一个询问点,如果询问的p是当前枚举的p的话,把num[q[i].v]加到ans[q[i].id]里。
复杂度O(n*100)
p>100时,发现,10000/p<100
也就是说,如果x=p*k+v,那么,这个k最多是100
对于这种情况,可以再询问离线拆分从前往后扫描。
num[j]记录,数值为j的数出现多少个。
到达一个询问点的时候,枚举k,统计所有的p*k+v的数个数总和。
复杂度O(n*100)
代码:
注意指针L的边界移动条件等等。
#include<bits/stdc++.h>
using namespace std;
const int N=100000+5;
const int M=10000+5;
int n,m;
int a[N];
int mx;
struct que{
int pos,v;
int ad;
int id;
int mod;
void init(int a,int b,int c,int d,int e){
pos=a,v=b,ad=c,id=d,mod=e;
}
bool friend operator <(que a,que b){
return a.pos<b.pos;
}
}q1[2*N],q2[2*N];
int cnt1,cnt2;
int num[M];
int ans[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),mx=max(mx,a[i]);
int l,r,p,v;
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&l,&r,&p,&v);
if(p<=100){
if(l-1)q1[++cnt1].init(l-1,v,-1,i,p);
q1[++cnt1].init(r,v,1,i,p);
}
else{
if(l-1)q2[++cnt2].init(l-1,v,-1,i,p);
q2[++cnt2].init(r,v,1,i,p);
}
}
sort(q1+1,q1+cnt1+1);
sort(q2+1,q2+cnt2+1);
for(int i=1;i<=100;i++){
memset(num,0,sizeof num);
int L=0;
for(int j=1;j<=cnt1;j++){
while(L<q1[j].pos){
L++;
num[a[L]%i]++;
}
if(q1[j].mod==i){
ans[q1[j].id]+=q1[j].ad*num[q1[j].v];
}
}
}
memset(num,0,sizeof num);
int L=0;
for(int j=1;j<=cnt2;j++){
while(L<q2[j].pos){
//cout<<L<<" : "<<a[L]<<endl;
L++;
num[a[L]]++;
}
//cout<<" L "<<L<<endl;
for(int l=0;q2[j].mod*l+q2[j].v<=mx;l++){
//cout<<q2[j].pos<<" "<<q2[j].id<<" "<<q2[j].ad<<" "<<q2[j].mod<<" "<<q2[j].v<<endl;
//cout<<" "<<q2[j].mod*l+q2[j].v<<" "<<num[q2[j].mod*l+q2[j].v]<<endl;
ans[q2[j].id]+=q2[j].ad*num[q2[j].mod*l+q2[j].v];
}
}
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]);
}
return 0;
}
当然,我们也可以牺牲一个logn在线处理
仍然要对p分块
i207M的做法:
1.p<=100,对序列也分块。f[i][j][k]表示,前i块,mod p=k的数有多少个。
然后块内查询,块外暴力即可。
2.p>100,用一个vector<int>mem[10000]记录,数值为x的数出现的位置。
从前到后扫描,自然排好了序。
然后对于询问,x=p*k+v,
同样利用k<=100,枚举k,然后在mem[p*k+v]内进行二分。找到[l,r]内包含的数。
即可统计。
复杂度O(M*100*logn)(这个logn其实很小,因为每个vector的size总和是n)
也可以通过。
总结:
这个题是这场比赛失败的差距所在。
对分块的运用不够灵活,对p分块是经典的操作,但是竟然不熟悉。。。。
p<=sqrt(n),余数<=sqrt(n)
p>sqrt(n),x=p*k+v的k也<=sqrt(n)
也是一种平均一下复杂度,达到msqrt(n)的均衡。