题解——[AHOI2013]作业(莫队)
题解——[AHOI2013]作业(莫队)
有一段时间没写莫队,今天WZB分享这道题,ssw02一看,我可以用莫队水,写的挺快的
欢迎转载ssw02的博客:https://www.cnblogs.com/ssw02/p/11823191.html
题面
给定长为N的序列 , M个询问,每次询问在 下标在[ L , R ]之间 , 数值在 [ a , b ]之间的数的种类和总数 。
N, M都是在1e5 的范围内 。
思路
可以离线,数据支持根号算法,所以我们可以考虑分块 。 总数和如何统计 , 开个桶记录数值的个数 。 每次修改时用一个树状数组维护。支持区间查询即可 。
有一点比较坑人, 之所以种类ssw02也用树状数组维护是因为只有[ a, b ]之间的修改才对当前答案有影响,所以我们不能够直接修改,应为上一次的种类并不能够继承到这次。我们需要重新统计(正好写了树状数组嘛)。另一种方法是在l , r移动的时候顺带处理不合法的贡献,把这些贡献删掉 。 由于数据较小,所以ssw02直接用2个树状数组分别维护2个答案即可 。
代码
#include<bits/stdc++.h>
using namespace std ;
#define ll long long
const int MAXN = 100005 ;
inline int read(){
int s=0 ; char g=getchar() ; while( g>'9'||g<'0')g=getchar() ;
while( g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s;
}
int N , M , a[MAXN] , tot , block , pos[MAXN] , vis[MAXN] ;
ll cnt[MAXN] , c[MAXN] , ans[MAXN] , anss[MAXN] ;
struct ap{
int l , r , id , a , b ;
}t[MAXN] ;
//-----------------
void add( int x , int y ){
for( ; x <= N ; x += x&-x )c[x] += y ;
}
void add2( int x, int y ){
for( ; x <= N ; x += x&-x )vis[x] += y ;
}
ll ask_sum( int x ){
ll tott = 0LL ;
for( ; x ; x -= x&-x )
tott += c[x] ;
return tott ;
}
ll ask_col( int x ){
ll tott = 0LL ;
for( ; x ; x -= x&-x )tott += vis[x] ;
return tott ;
}
//-----------------
inline bool cmp( ap x , ap y ){
return ( pos[x.l] == pos[y.l] )?(x.r < y.r):(pos[x.l] < pos[y.l] ) ;
}
void updata( int u , int opt , int now ){
if( opt ){
if( cnt[a[u]] == 0 )add2( a[u] , 1 ) ;
cnt[ a[u] ]++ ; add( a[u] , 1 ) ;
return ;
}
else {
if( cnt[a[u]] == 1 )add2( a[u] , -1 ) ;
cnt[ a[u] ]-- ; add( a[u] , -1 ) ;
}
}
ll ask( int l , int r , int opt ){
if(opt)return ask_sum( r ) - ask_sum( l-1 ) ;
else return ask_col( r ) - ask_col( l-1 ) ;
}
void Mo(){
for( int i = 1 , l = 1 , r = 0 ; i <= M ; ++i ){
while( t[i].l > l )updata( l++ , 0 , i ) ;
while( t[i].l < l )updata( --l , 1 , i ) ;
while( t[i].r > r )updata( ++r , 1 , i ) ;
while( t[i].r < r )updata( r-- , 0 , i ) ;
//for( int k = 1 ; k <= N ; ++k )cout<<c[k]<<" "; cout<<endl ;
anss[ t[i].id ] = ask( t[i].a , t[i].b , 0 ) , ans[ t[i].id ] = ask( t[i].a , t[i].b , 1 ) ;
}
}
int main(){
N = read() , M = read() , block = sqrt(N) ;
for( int i = 1 ; i <= N ; ++i )a[i] = read() , pos[i] = ( i-1 )/block+1 ;
for( int i = 1 ; i <= M ; ++i ){
t[i].l = read() , t[i].r = read() , t[i].a = read() , t[i].b = read() , t[i].id = i ;
}
sort( t+1 , t+M+1 , cmp ) ;
Mo() ;
for( int i = 1 ; i <= M ; ++i )
printf("%lld %lld\n",ans[i],anss[i]) ;
}