题解 P4396 [AHOI2013]作业
https://www.luogu.com.cn/problem/P4396
一道用值域分块维护莫队的模板题。
还是用值域分块把,用树状数组会T。
不过还是贴一下树状数组的代码。
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define LL long long
#define rint register int
using namespace std;
namespace FastIO
{
const int _SIZE = (1 << 21) + 1;
char ibuf[_SIZE],obuf[_SIZE];
char *iS,*iT;
char c;
char qu[55];
char *oS=obuf,*oT=oS+_SIZE-1;
bool _sign=false;
int qr;
// getchar
#define gc() (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, _SIZE, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
// print the remaining part
inline void flush()
{
fwrite(obuf,1,oS-obuf,stdout);
oS=obuf;
return;
}
// putchar
inline void putc(const char &x)
{
*oS++=x;
if(oS==oT)
flush();
return;
}
inline char getc()
{
return gc();
}
// input a signed integer
template <class T>
inline void read(T &x)
{
x=0;
_sign=false;
for (c=gc();c<'0'||c>'9';c=gc())
if (c=='-')
_sign=true;
for (;c>='0'&&c<='9';c=gc())
x=(x<<1)+(x<<3)+(c&15);
x=(_sign) ? (~x+1) : x;
return;
}
// print a signed integer
template <class T>
inline void print(T x) {
if (!x) {
putc('0');
return;
}
if (x<0)
putc('-'),x=~x+1;
while(x) qu[++qr]=x%10+'0',x/=10;
while(qr) putc(qu[qr--]);
return;
}
// no need to call flush at the end manually!
struct Flusher_
{
~Flusher_() { flush(); }
}io_flusher_;
} // namespace io
using FastIO::read;
using FastIO::print;
using FastIO::putc;
using FastIO::getc;
//=================================================
const int N=1e5+5;
int n,m;
struct Bits
{
int c[N];
int res;
inline int lowbit(const int &x) { return x&(-x); }
inline void update(int x,int y)
{
for(;x<=n;x+=lowbit(x))
c[x]+=y;
return;
}
inline int sum(int x)
{
res=0;
for(;x>=1;x-=lowbit(x))
res+=c[x];
return res;
}
}C1,C2;
int a[N];
int t[N];
struct quiz
{
int l,r;
int a,b;
int ans1,ans2;
int o;
}q[N];
int siz;
inline bool cmp(const quiz &a,const quiz &b)
{
if(a.l/siz!=b.l/siz)
return a.l<b.l;
else return a.r<b.r;
}
inline bool rule(const quiz &a,const quiz &b)
{
return a.o<b.o;
}
inline void Add(int pos)
{
if(t[a[pos]]==0)
C2.update(a[pos],1);
C1.update(a[pos],1);
++t[a[pos]];
return;
}
inline void Sub(int pos)
{
if(t[a[pos]]==1)
C2.update(a[pos],-1);
C1.update(a[pos],-1);
--t[a[pos]];
return;
}
int main()
{
// freopen("1.in","r",stdin);
rint i;
read(n); read(m);
for(i=1;i<=n;i++)
read(a[i]);
for(i=1;i<=m;i++) {
read(q[i].l); read(q[i].r);
read(q[i].a); read(q[i].b);
q[i].o=i;
}
siz=sqrt(n);
sort(q+1,q+m+1,cmp);
rint head=1,tail=0;
for(i=1;i<=m;i++) {
while(head>q[i].l) Add(--head);
while(tail<q[i].r) Add(++tail);
while(head<q[i].l) Sub(head++);
while(tail>q[i].r) Sub(tail--);
q[i].ans1=C1.sum(q[i].b)-C1.sum(q[i].a-1);
q[i].ans2=C2.sum(q[i].b)-C2.sum(q[i].a-1);
}
sort(q+1,q+m+1,rule);
for(i=1;i<=m;i++) {
print(q[i].ans1); putc(' ');
print(q[i].ans2); putc('\n');
}
return 0;
}
时间复杂度 \(O((m+n)\sqrt{n}logn+m(logn+logC)\) (C为值域)
开 \(o2\) 过的。
值域分块
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define LL long long
#define rint register int
using namespace std;
namespace FastIO
{
const int _SIZE = (1 << 21) + 1;
char ibuf[_SIZE],obuf[_SIZE];
char *iS,*iT;
char c;
char qu[55];
char *oS=obuf,*oT=oS+_SIZE-1;
bool _sign=false;
int qr;
// getchar
#define gc() (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, _SIZE, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
// print the remaining part
inline void flush()
{
fwrite(obuf,1,oS-obuf,stdout);
oS=obuf;
return;
}
// putchar
inline void putc(const char &x)
{
*oS++=x;
if(oS==oT)
flush();
return;
}
inline char getc()
{
return gc();
}
// input a signed integer
template <class T>
inline void read(T &x)
{
x=0;
_sign=false;
for (c=gc();c<'0'||c>'9';c=gc())
if (c=='-')
_sign=true;
for (;c>='0'&&c<='9';c=gc())
x=(x<<1)+(x<<3)+(c&15);
x=(_sign) ? (~x+1) : x;
return;
}
// print a signed integer
template <class T>
inline void print(T x) {
if (!x) {
putc('0');
return;
}
if (x<0)
putc('-'),x=~x+1;
while(x) qu[++qr]=x%10+'0',x/=10;
while(qr) putc(qu[qr--]);
return;
}
// no need to call flush at the end manually!
struct Flusher_
{
~Flusher_() { flush(); }
}io_flusher_;
} // namespace io
using FastIO::read;
using FastIO::print;
using FastIO::putc;
using FastIO::getc;
//=================================================
const int N=1e5+5;
const int SQ=1e3+5;
int n,m;
int F[N];
int L[SQ],R[SQ];
int size,k;
void Build()
{
size=sqrt(N);
k=N/size+(N/size>0);
rint i,j;
for(i=1;i<=k;i++) {
L[i]=R[i-1]+1;
R[i]=i*size;
}
R[k]=N-1;
for(i=1;i<=k;i++)
for(j=L[i];j<=R[i];j++)
F[j]=i;
return;
}
struct Block
{
int num[SQ];
int t[N];
inline void insert(const int &val)
{
t[val]++;
num[F[val]]++;
return;
}
inline void erase(const int &val)
{
t[val]--;
num[F[val]]--;
return;
}
inline int count(const int &l,const int &r)
{
rint i,res=0;
if(F[l]==F[r]) {
for(i=l;i<=r;i++)
res+=t[i];
return res;
}
for(i=l;i<=R[F[l]];i++)
res+=t[i];
for(i=F[l]+1;i<=F[r]-1;i++)
res+=num[i];
for(i=L[F[r]];i<=r;i++)
res+=t[i];
return res;
}
}A,B;
int a[N];
struct quiz
{
int l,r;
int a,b;
int ans1,ans2;
int o;
}q[N];
int siz;
inline bool cmp(const quiz &a,const quiz &b)
{
if(a.l/siz!=b.l/siz)
return a.l<b.l;
else return a.r<b.r;
}
inline bool rule(const quiz &a,const quiz &b)
{
return a.o<b.o;
}
inline void Add(int pos)
{
if(A.count(a[pos],a[pos])==0)
B.insert(a[pos]);
A.insert(a[pos]);
return;
}
inline void Sub(int pos)
{
if(A.count(a[pos],a[pos])==1)
B.erase(a[pos]);
A.erase(a[pos]);
return;
}
int main()
{
// freopen("1.in","r",stdin);
rint i;
read(n); read(m);
for(i=1;i<=n;i++)
read(a[i]);
for(i=1;i<=m;i++) {
read(q[i].l); read(q[i].r);
read(q[i].a); read(q[i].b);
q[i].o=i;
}
Build();
siz=sqrt(n);
sort(q+1,q+m+1,cmp);
rint head=1,tail=0;
for(i=1;i<=m;i++) {
while(head>q[i].l) Add(--head);
while(tail<q[i].r) Add(++tail);
while(head<q[i].l) Sub(head++);
while(tail>q[i].r) Sub(tail--);
q[i].ans1=A.count(q[i].a,q[i].b);
q[i].ans2=B.count(q[i].a,q[i].b);
}
sort(q+1,q+m+1,rule);
for(i=1;i<=m;i++) {
print(q[i].ans1); putc(' ');
print(q[i].ans2); putc('\n');
}
return 0;
}
时间复杂度:\(O((n+m)\sqrt{n}+m\sqrt{C}+mlogn)\)
使用值域分块能达到复杂度的平衡。(指针移动 \(O((n+m)\sqrt{n})\),求解 \(O(m\sqrt{C})\))
写分块一定要记得特判最后一个块,
k=N/size+(N/size>0);
R[k]=N-1;
不然要么开太小了,要么越界。