莫队
莫队是个维护区间操作的好东西,它采用分块的思想,每次暴力跳区间,使得指针移动次数为 \(\mathcal O(q\sqrt{n})\),不得不说莫涛还是太强了。
常见的莫队是普通莫队,莫队二次离线,树上莫队,回滚莫队,带修莫队。莫队最好的是能够快速获得区间内的信息,,如果发现指针的移动可以i \(\mathcal O(1)\) 实现,那么我们可以考虑莫队。
普通莫队没啥好讲的,莫队二次离线讲题的时候再说。
树上莫队简单来说就是把树推成欧拉序或者 DFS 序,都可以,看喜好。
回滚莫队就是可以回滚的。我们将序列分块,然后找到左端点所在的块的最右边的序号。对于左端点同一块内的右端点 \(p\) 而言,他们是单增的(注意,除了回滚莫队不可以蝶形优化,其他的都可以),所以没有回滚的意义。对于左端点而言,他可能一会左一会右。这个时候我们可以把序列拆为 \([l,p]\) 和 \([p+1,r]\),后面那一段好处理因为 \(r\) 不降,而前面的那一段呢?很简单,我们只需要每一次暴力的维护 \([l,p]\) 即可,然后暴力的把 \([l,p]\) 的信息清空,这样的话暴力时间也是 \(mathcal O(\sqrt{n})\),是完全可以接受的。
带修莫队就是带修改的莫队,加上修改一维即可。
P1972 [SDOI2009] HH的项链
虽然卡莫队,但不失为一道莫队练手题(毕竟可以卡过去,跑的比主席树还快)。
开一个桶,由于桶移动指针的时候至于删掉/增加的元素有关,所以是 \(mathcal O(1)\) 修改的,直接莫队即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,q,len,pos[N],b[N],c[N],cnt,ans[N],nxt[N],lst[N],pre[N];
namespace Fast_I {
char *_Buf, *_Start_ptr, *_End_ptr;
std::streambuf* inbuf;
unsigned int Size;
bool _Ok;
struct Fast_Istream {
operator bool() { return _Ok; }
Fast_Istream(std::streambuf*, unsigned int);
Fast_Istream(unsigned int);
Fast_Istream(const char*, unsigned int);
Fast_Istream& operator>>(char&);
Fast_Istream& operator>>(char*);
Fast_Istream& operator>>(bool&);
Fast_Istream& operator>>(short&);
Fast_Istream& operator>>(int&);
Fast_Istream& operator>>(long&);
Fast_Istream& operator>>(long long&);
Fast_Istream& operator>>(unsigned short&);
Fast_Istream& operator>>(unsigned int&);
Fast_Istream& operator>>(unsigned long&);
Fast_Istream& operator>>(unsigned long long&);
Fast_Istream& operator>>(float&);
Fast_Istream& operator>>(double&);
Fast_Istream& operator>>(long double&);
Fast_Istream& operator>>(std::string&);
template <typename Typex>
void operator()(Typex& _Val) { *this >> _Val; }
template <typename Typex, typename... More>
void operator()(Typex&, More&...);
std::streambuf* rdbuf() { return inbuf; }
void rdbuf(std::streambuf* _inbuf) { inbuf = _inbuf; }
void rdbuf(const char*);
void pop();
char peek();
};
} // namespace Fast_I
namespace Fast_O {
std::string buf;
std::streambuf* outbuf;
struct Fast_Ostream {
Fast_Ostream(std::streambuf*, unsigned int);
Fast_Ostream(std::streambuf* out) { outbuf = out; }
Fast_Ostream(const char*, unsigned int);
Fast_Ostream(unsigned int);
void flush();
~Fast_Ostream();
void endl() { buf.push_back('\n'); }
template <typename Typex>
void endl(const Typex& _Val);
template <typename Typex, typename... More>
void endl(const Typex&, const More&...);
template <typename Typex>
void operator()(const Typex& _Val);
template <typename Typex, typename... More>
void operator()(const Typex&, const More&...);
Fast_Ostream& operator<<(char);
Fast_Ostream& operator<<(const char*);
Fast_Ostream& operator<<(const std::string&);
Fast_Ostream& operator<<(bool);
Fast_Ostream& operator<<(short);
Fast_Ostream& operator<<(int);
Fast_Ostream& operator<<(long);
Fast_Ostream& operator<<(long long);
Fast_Ostream& operator<<(unsigned short);
Fast_Ostream& operator<<(unsigned int);
Fast_Ostream& operator<<(unsigned long);
Fast_Ostream& operator<<(unsigned long long);
std::streambuf* rdbuf() { return outbuf; }
void rdbuf(std::streambuf* _outbuf) { outbuf = _outbuf; }
void rdbuf(const char*);
};
} // namespace Fast_O
namespace Fast_IO {
Fast_I::Fast_Istream fin(std::cin.rdbuf(), 1048576); // 1 MB
Fast_O::Fast_Ostream fout(std::cout.rdbuf()); // ∞
} // namespace Fast_IO
#define cin Fast_IO::fin
#define cout Fast_IO::fout
namespace Fast_I {
Fast_Istream::Fast_Istream(std::streambuf* in, unsigned int Sz) {
_Ok = 1;
Fast_I::Size = Sz;
inbuf = in;
_Start_ptr = _End_ptr = _Buf = new char[Sz];
}
Fast_Istream::Fast_Istream(const char* in, unsigned int Sz) {
_Ok = 1;
Fast_I::Size = Sz;
rdbuf(in);
_Start_ptr = _End_ptr = _Buf = new char[Sz];
}
Fast_Istream::Fast_Istream(unsigned int Sz) {
_Ok = 1;
Fast_I::Size = Sz;
_Start_ptr = _End_ptr = _Buf = new char[Sz];
}
void Fast_Istream::rdbuf(const char* File) {
static std::ifstream __In__(File);
rdbuf(__In__.rdbuf());
}
void Get_Char(char& _Val) {
if (_Start_ptr == _End_ptr) {
_Start_ptr = _Buf;
_End_ptr = _Buf + inbuf->sgetn(_Buf, Size);
}
if (_Start_ptr == _End_ptr) {
_Val = -1;
_Ok = 0;
} else {
_Val = *_Start_ptr++;
}
}
Fast_Istream& Fast_Istream::operator>>(char& _Val) {
if (_Ok) {
Get_Char(_Val);
while (_Val == 32 || _Val == 10 || _Val == 13 || _Val == 8 || _Val == 9 ||
_Val == 7 || _Val == 12 || _Val == 11) {
Get_Char(_Val);
}
}
return *this;
}
Fast_Istream& Fast_Istream::operator>>(char* _Val) {
if (_Ok) {
Get_Char(*_Val);
while (*_Val == 32 || *_Val == 10 || *_Val == 13 || *_Val == 8 ||
*_Val == 9 || *_Val == 7 || *_Val == 12 || *_Val == 11) {
Get_Char(*_Val);
}
while (*_Val != 32 && *_Val != 10 && *_Val && *_Val != -1 && *_Val != 9 &&
*_Val != 11 && *_Val != 12) {
Get_Char(*++_Val);
}
*_Val = 0;
--_Start_ptr;
}
return *this;
}
Fast_Istream& Fast_Istream::operator>>(std::string& _Val) {
if (_Ok) {
char c;
Get_Char(c);
while (c == 32 || c == 10 || c == 13 || c == 8 || c == 9 || c == 7 ||
c == 12 || c == 11) {
Get_Char(c);
}
for (_Val.clear();
c != 32 && c != 10 && c && c != -1 && c != 9 && c != 11 && c != 12;
Get_Char(c)) {
_Val.push_back(c);
}
--_Start_ptr;
}
return *this;
}
template <typename Typex>
void Get_Int(Typex& _Val) {
if (_Ok) {
char ch;
bool _F = 0;
for (Get_Char(ch); (ch < 48 || ch > 57) && ch != -1; Get_Char(ch)) {
_F = ch == 45;
}
for (_Val = 0; ch > 47 && ch < 58 && ch != -1; Get_Char(ch)) {
_Val = _Val * 10 + (ch ^ 48);
}
if (_F) {
_Val = ~_Val + 1;
}
--_Start_ptr;
}
}
Fast_Istream& Fast_Istream::operator>>(bool& _Val) {
if (_Ok) {
char ch;
Get_Char(ch);
while (ch == 32 || ch == 10 || ch == 13 || ch == 8 || ch == 9 || ch == 7 ||
ch == 12 || ch == 11) {
Get_Char(ch);
}
while (ch != 32 && ch != 10 && ch && ch != -1 && ch != 9 && ch != 11 &&
ch != 12) {
_Val |= ch != 48;
Get_Char(ch);
}
--_Start_ptr;
}
return *this;
}
Fast_Istream& Fast_Istream::operator>>(int& _Val) {
Get_Int(_Val);
return *this;
}
Fast_Istream& Fast_Istream::operator>>(long long& _Val) {
Get_Int(_Val);
return *this;
}
template <typename Typex, typename... More>
void Fast_Istream::operator()(Typex& _Val, More&... _More) {
*this >> _Val;
operator()(_More...);
}
void Fast_Istream::pop() {
char ch;
Get_Char(ch);
}
char Fast_Istream::peek() {
if (_Start_ptr == _End_ptr) {
_Start_ptr = _Buf;
_End_ptr = _Buf + inbuf->sgetn(_Buf, Size);
}
if (_Start_ptr == _End_ptr) {
_Ok = 0;
return -1;
} else {
return *_Start_ptr;
}
}
} // namespace Fast_I
namespace Fast_O {
Fast_Ostream::Fast_Ostream(std::streambuf* out, unsigned int Size) {
buf.reserve(Size);
outbuf = out;
}
Fast_Ostream::Fast_Ostream(const char* File, unsigned int Size) {
buf.reserve(Size);
rdbuf(File);
}
void Fast_Ostream::rdbuf(const char* File) {
static std::ofstream __Out__(File);
rdbuf(__Out__.rdbuf());
}
Fast_Ostream::Fast_Ostream(unsigned int Size) {
buf.reserve(Size);
}
void Fast_Ostream::flush() {
outbuf->sputn(buf.data(), buf.size());
buf.clear();
}
Fast_Ostream::~Fast_Ostream() {
flush();
}
Fast_Ostream& Fast_Ostream::operator<<(char _Val) {
buf.push_back(_Val);
return *this;
}
Fast_Ostream& Fast_Ostream::operator<<(const char* _Val) {
while (*_Val) {
buf.push_back(*_Val++);
}
return *this;
}
Fast_Ostream& Fast_Ostream::operator<<(const std::string& _Val) {
for (auto&& i : _Val) {
buf.push_back(i);
}
return *this;
}
template <typename Typex>
void Put_Unsigned(Typex _Val) {
char* _Stack = (char*)malloc(sizeof(Typex) * 3);
unsigned S_top = 0;
while (_Val) {
_Stack[++S_top] = (_Val % 10) ^ 48;
_Val /= 10;
}
if (!S_top) {
buf.push_back('0');
}
while (S_top) {
buf.push_back(_Stack[S_top--]);
}
free(_Stack);
}
void Put_Int(long long _Val) {
if (_Val < 0) {
buf.push_back('-');
Put_Unsigned(~_Val + 1);
} else {
Put_Unsigned(_Val);
}
}
Fast_Ostream& Fast_Ostream::operator<<(int _Val) {
Put_Int(_Val);
return *this;
}
Fast_Ostream& Fast_Ostream::operator<<(long long _Val) {
Put_Int(_Val);
return *this;
}
Fast_Ostream& Fast_Ostream::operator<<(unsigned int _Val) {
Put_Unsigned(_Val);
return *this;
}
template <typename Typex>
void Fast_Ostream::endl(const Typex& _Val) {
*this << _Val << '\n';
}
template <typename Typex, typename... More>
void Fast_Ostream::endl(const Typex& _Val, const More&... _More) {
*this << _Val;
endl(_More...);
}
template <typename Typex>
void Fast_Ostream::operator()(const Typex& _Val) {
*this << _Val;
}
template <typename Typex, typename... More>
void Fast_Ostream::operator()(const Typex& _Val, const More&... _More) {
*this << _Val;
operator()(_More...);
}
} // namespace Fast_O
struct node
{
int l,r,id;
friend bool operator < (const node &x,const node &y)
{
if(pos[x.l]!=pos[y.l])
return pos[x.l]<pos[y.l];
if(pos[x.l]&1)
return x.r<y.r;
return x.r>y.r;
}
}a[N];
int main()
{
cin(n);
for(int i=1;i<=n;i++)
cin(b[i]);
for(int i=n;i;i--)
{
if(!lst[b[i]]) nxt[i]=n+1,lst[b[i]]=i;
else nxt[i]=lst[b[i]],lst[b[i]]=i;
}
memset(lst,0,sizeof(lst));
for(int i=1;i<=n;i++)
{
if(!lst[b[i]]) pre[i]=0,lst[b[i]]=i;
else pre[i]=lst[b[i]],lst[b[i]]=i;
}
cin(q);int len=n/sqrt(q);
for(int i=1;i<=n;i++)
pos[i]=(i-1)/len+1;
for(int i=1;i<=q;i++)
{
cin(a[i].l,a[i].r);
a[i].id=i;
}
sort(a+1,a+1+q);
for(int i=1,L=0,R=0;i<=q;i++)
{
int l=a[i].l,r=a[i].r;
while(L>l){L--;cnt+=(nxt[L]>R);}
while(L<l){cnt-=(nxt[L]>R);L++;};
while(R>r){cnt-=(pre[R]<l);R--;}
while(R<r){R++;cnt+=(pre[R]<l);}
// while(L>l) L--,cnt+=!c[b[L]],c[b[L]]++;
// while(L<l) cnt-=(c[b[L]]==1),c[b[L]]--,L++;
// while(R>r) cnt-=(c[b[R]]==1),c[b[R]]--,R--;
// while(R<r) R++,cnt+=!c[b[R]],c[b[R]]++;
ans[a[i].id]=cnt;
}
for(int i=1;i<=q;i++)
cout.endl(ans[i]);
return 0;
}
P4887 【模板】莫队二次离线(第十四分块(前体))
我们来讲讲什么是莫队二次离线。简单来说,他的思想就是前缀和。我们假设位置 \(i\) 在 \([l,r]\) 上的贡献为 \(f_{x,l,r}\),那么我们就是要求 \(\sum\limits_{i=l+1}^r f_{i,l,i-1}\)。
对于 \(f\) 来说,我们使用前缀和变为 \(f_{i,1,i-1}-f_{i,1,l-1}\)。
显然对于 \(f_{i,1,i-1}\) 我们可以预处理。具体方法是先把 \(1\) 的个数为 \(k\) 的数存起来,然后开一个桶 \(b\),扫一遍数组,扫到 \(i\) 时,将 \(b[a_i^x]++\) (\(x\) 为存起来的数),那么 \(f_{i+1,1,i}\) 就是 \(b[a[i+1]]\)。
那么现在的关键就是 \(f_{i,1,l-1}\) 了。想来聪明的你也就会了,像扫描线那样把这个询问挂在 \(l-1\) 上做扫描线就行了,这样这道题就做完了。
完结撒花。
并非完结。我们这么做有个漏洞,那就是我们在 \(i\) 询问时指针 \(l,r\) 移动的时候真的只是对询问 \(i\) 吗?其实不是的,想想看,这个指针移动其实是针对 \([i,m]\) 所有询问,所以最后需要求一个前缀和,然后就无了。
复杂度是 \(\mathcal O(\binom{14}{k}n+n\sqrt{n})\)。
#include<bits/stdc++.h>
#define i64 int64_t
using namespace std;
const int N=100005,B=320;
int p[N],a[N],n,m,k,bu[N],lst[N];
i64 ans[N];
struct Qry
{
int l,r,id;i64 ans;
friend bool operator<(const Qry &A,const Qry &B)
{
if(p[A.l]!=p[B.l]) return p[A.l]<p[B.l];
if(p[A.l]&1) return A.r<B.r;
return A.r>B.r;
}
}Q[N];
vector<tuple<int,int,int>> G[N];
vector<int> tmp;
int main()
{
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++)
cin>>Q[i].l>>Q[i].r,Q[i].id=i;
if(k>14)
{
for(int i=1;i<=m;++i) puts("0");
return 0;
}
for (int i=0;i<16384;++i)
if (__builtin_popcount(i)==k) tmp.push_back(i);
for (int i=1;i<=n;++i) p[i]=(i-1)/B+1;
sort(Q+1,Q+m+1);
for(int i=1;i<=n;i++)
{
for(int x:tmp) bu[a[i]^x]++;
lst[i]=bu[a[i+1]];
}
for(int i=1,L=1,R=0;i<=m;i++)
{
int l=Q[i].l,r=Q[i].r,id=Q[i].id;
if (L<l) G[R].emplace_back(L,l-1,-i);
while (L<l) {Q[i].ans+=lst[L-1];L++;}
if (L>l) G[R].emplace_back(l,L-1,i);
while (L>l) {Q[i].ans-=lst[L-2];L--;}
if (R<r) G[L-1].emplace_back(R+1,r,-i);
while (R<r) {Q[i].ans+=lst[R];R++;}
if (R>r) G[L-1].emplace_back(r+1,R,i);
while (R>r) {Q[i].ans-=lst[R-1];R--;}
}
memset(bu,0,sizeof(bu));
for(int i=1;i<=n;i++)
{
for(int x:tmp) bu[a[i]^x]++;
int l,r,id;
for(tuple<int,int,int> TMP:G[i])
{
tie(l,r,id)=TMP;
for(int j=l;j<=r;j++)
{
int x=bu[a[j]];
if(j<=i&&!k) x--;//不能算上自己,小细节,要注意
if(id<0) Q[-id].ans-=x;
else Q[id].ans+=x;
}
}
}
for(int i=1;i<=m;i++) Q[i].ans+=Q[i-1].ans;
for(int i=1;i<=m;i++) ans[Q[i].id]=Q[i].ans;
for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
return 0;
}
P4867 Gty的妹子序列
莫队,然后我们就会发现这时需要求值在 \([a,b]\) 之间的数的个数。而莫队修改是 \(\mathcal O(1)\) 的,为了调节复杂度,我们可以选择用值域分块。或者你可以直接树状数组,也可以,反正这道题不卡 \(\mathcal O(n\sqrt{n}\log n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=1e6+5,B=450;
int n,m,s[N],pos[N],ans[M],cnt[N];
struct Q
{
int id,l,r,a,b;
friend bool operator<(const Q &x,const Q &y)
{
if(pos[x.l]!=pos[y.l])
return pos[x.l]<pos[y.l];
if(pos[x.l]&1)
return x.r<y.r;
return x.r>y.r;
}
}q[M];
struct BIT
{
int t[N];
int lb(int x){return x&-x;}
void upd(int x,int k)
{
if(!x) return;
for(;x<=n;x+=lb(x))
t[x]+=k;
}
int query(int x)
{
int res=0;
for(;x;x-=lb(x))
res+=t[x];
return res;
}
}T;
void ins(int id)
{
if(!cnt[s[id]]) T.upd(s[id],1);
cnt[s[id]]++;
}
void del(int id)
{
if(cnt[s[id]]==1) T.upd(s[id],-1);
cnt[s[id]]--;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>s[i];
for(int i=1;i<=n;i++)
pos[i]=(i-1)/B+1;
for(int i=1;i<=m;i++)
{
cin>>q[i].l>>q[i].r>>q[i].a>>q[i].b;
q[i].id=i;
}
sort(q+1,q+1+m);
for(int i=1,L=0,R=0;i<=m;i++)
{
int l=q[i].l,r=q[i].r,id=q[i].id,a=q[i].a,b=q[i].b;
while(L>l) L--,ins(L);
while(L<l) del(L),L++;
while(R<r) R++,ins(R);
while(R>r) del(R),R--;
ans[id]=T.query(b)-T.query(a-1);
}
for(int i=1;i<=m;i++)
cout<<ans[i]<<"\n";
return 0;
}
P2709 小B的询问
普通莫队,转移的话就是 \(x^2\rightarrow (x+1)^2\),直接 \(+2x+1\) 即可。减同理。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e4+5;
int n,m,k,a[N],c[N],ans,B,pos[N],num[N];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar();
return x*f;
}
inline void write(int x){
if(x<0) putchar('-'),x=-x;
if(x>=10) write(x/10);
putchar(x%10+'0');
}
inline void add(int pos){
ans-=c[pos]*c[pos];
c[pos]++;
ans+=c[pos]*c[pos];
}
inline void del(int pos){
ans-=c[pos]*c[pos];
c[pos]--;
ans+=c[pos]*c[pos];
}
struct query{
int l,r,id;
}q[N];
inline bool cmp(query x,query y){
if(pos[x.l]!=pos[y.l])
return pos[x.l]<pos[y.l];
if(pos[x.l]&1)
return x.r<y.r;
return x.r>y.r;
}
int L,R,l,r;
signed main(){
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
n=read(),m=read(),k=read();L=1,R=0;
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=m;i++){
l=read(),r=read();
q[i].l=l,q[i].r=r,q[i].id=i;
}
sort(q+1,q+1+m,cmp);
int B=sqrt(n);
for(int i=1;i<=n;i++)
pos[i]=(i-1)/B+1;
for(int i=1;i<=m;i++){
l=q[i].l,r=q[i].r;
while(L>l) L--,add(a[L]);
while(R<r) R++,add(a[R]);
while(L<l) del(a[L]),L++;
while(R>r) del(a[R]),R--;
num[q[i].id]=ans;
}
for(int i=1;i<=m;i++)
write(num[i]),puts("");
return 0;
}
P1903 [国家集训队] 数颜色 / 维护队列
带修板子。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5,M=1.4e5+5;
int n,m,a[M],pos[M],cntc,cntq,b[N],cnt,ans[M];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar();
return x*f;
}
struct node
{
int l,r,t,id;
friend bool operator <(const node &x,const node &y)
{
if(pos[x.l]!=pos[y.l]) return x.l<y.l;
if(pos[x.r]!=pos[y.r]) return x.r<y.r;
return x.t<y.t;
}
}q[M];
struct COL
{
int c,id;
}C[M];
void del(int x)
{
b[x]--;
if(!b[x]) cnt--;
}
void ins(int x)
{
if(!b[x]) cnt++;
b[x]++;
}
void upd(int L,int R,int T)
{
if(C[T].id>=L&&C[T].id<=R)
{
del(a[C[T].id]);
ins(C[T].c);
}
swap(a[C[T].id],C[T].c);
}
int main()
{
n=read(),m=read();int len=pow(n,2.0/3.0);
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++)
pos[i]=(i-1)/len+1;
while(m--)
{
char op;cin>>op;
int l=read(),r=read();
if(op=='Q')
{
q[++cntq].l=l,q[cntq].r=r;
q[cntq].id=cntq,q[cntq].t=cntc;
}
else
{
C[++cntc].c=r;
C[cntc].id=l;
}
}
sort(q+1,q+1+cntq);
int L=0,R=0,T=0;
for(int i=1;i<=cntq;i++)
{
int l=q[i].l,r=q[i].r,t=q[i].t;
while(L<l) del(a[L]),L++;
while(L>l) L--,ins(a[L]);
while(R<r) R++,ins(a[R]);
while(R>r) del(a[R]),R--;
while(T<t) T++,upd(L,R,T);
while(T>t) upd(L,R,T),T--;
ans[q[i].id]=cnt;
}
for(int i=1;i<=cntq;i++)
printf("%d\n",ans[i]);
return 0;
}
P4688 [Ynoi Easy Round 2016] 掉进兔子洞
这道题很好。
有一种常见套路叫做莫队+ bitset 优化。
最简单的情况就是用 bitset 获取区间出现的数的种类,但是这里也要存数的个数??
不担心,先考虑离散化,但是与之不同的是,我们离散化后的数是比该数小的数的个数和+1。
可能很抽象,这里举个例子:\(1,2,1000,2,345,4,5,4,51,134\) 离散化后就是 \(1,2,10,2,9,4,6,4,7,8\)。这样我们存 \(x\) 出现的第 \(y\) 个数时我们在 \(x+y\) 处标记为 \(1\) 即可。而且可以保证这样离散化后数的大小不会超过序列长度。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=25005,B=320;
int n,a[N],lst,b[N],p[N],m,num=0,cnt[N],flag[M],len[M],ID;
bitset<N> bs[M],f;
map<int,int> mp;
set<int> s;
struct Q
{
int l,r,id,flag;
friend bool operator<(const Q &x,const Q &y)
{
if(p[x.l]!=p[y.l]) return p[x.l]<p[y.l];
if(p[x.l]&1) return x.r<y.r;
return x.r>y.r;
}
}q[M*3];
void ins(int x)
{
if(!x) return;
cnt[a[x]]++;
f[a[x]+cnt[a[x]]-1]=1;
}
void del(int x)
{
if(!x) return;
cnt[a[x]]--;
f[a[x]+cnt[a[x]]]=0;
}
void solve()
{
sort(q+1,q+1+num),f=0;
memset(cnt,0,sizeof(cnt));
memset(flag,0,sizeof(flag));
memset(len,0,sizeof(len));
for(int i=1,L=1,R=0;i<=num;i++)
{
int l=q[i].l,r=q[i].r,id=q[i].id;
while(L>l) L--,ins(L);
while(R<r) R++,ins(R);
while(L<l) del(L),L++;
while(R>r) del(R),R--;
if(!flag[id]) flag[id]=1,bs[id]=f;
else bs[id]&=f;
len[id]+=r-l+1;
}
for(int i=1;i<=ID;i++)
cout<<len[i]-3*bs[i].count()<<"\n";
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s.insert(a[i]);
mp[a[i]]++;
}
for(int x:s) b[x]=lst,lst+=mp[x];
for(int i=1;i<=n;i++) a[i]=b[a[i]];
for(int i=1;i<=n;i++) p[i]=(i-1)/B+1;
while(m--)
{
ID++,num++,cin>>q[num].l>>q[num].r,q[num].id=ID;
num++,cin>>q[num].l>>q[num].r,q[num].id=ID;
num++,cin>>q[num].l>>q[num].r,q[num].id=ID;
if(num==75000) solve(),num=ID=0;
}
if(num) solve();
return 0;
}
P1494 [国家集训队] 小 Z 的袜子
总的情况就是 \(\frac{len(len-1)}{2}\),考虑所求情况数。
普通莫队维护区间不同种类的出现次数,用桶维护即可。考虑有 \(x\) 种不同的袜子,每种个数是 \(a_i\),那么所求即为:
考虑怎么 \(\mathcal O(1)\) 修改,我们发现,每一次修改只会影响一种,若是加,则 \(\frac{a_i(a_i+1)}{2}\rightarrow \frac{(a_i+1)(a_i+2)}{2}\),发现增量是 \(a_i+1\),减同理,然后就做完了。
注意 long long。
#include<bits/stdc++.h>
#define int long long
#define f first
#define s second
#define pii pair<int,int>
using namespace std;
const int N=50005;
int n,m,a[N],pos[N],b[N],cnt;
pii ans[N];
struct node
{
int l,r,id;
friend bool operator <(const node &x,const node &y)
{
if(pos[x.l]!=pos[y.l]) return x.l<y.l;
if(pos[x.l]&1) return x.r<y.r;
return x.r>y.r;
}
}q[N];
void del(int x)
{
if(b[x]>=2) cnt-=b[x]*(b[x]-1)/2;
b[x]--;
if(b[x]>=2) cnt+=b[x]*(b[x]-1)/2;
}
void ins(int x)
{
if(b[x]>=2) cnt-=b[x]*(b[x]-1)/2;
b[x]++;
if(b[x]>=2) cnt+=b[x]*(b[x]-1)/2;
}
signed main()
{
scanf("%d%d",&n,&m);int len=sqrt(n);
for(int i=1;i<=n;i++)
pos[i]=(i-1)/len+1;
for(int i=1;i<=n;i++)
scanf("%d",a+i);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q+1,q+1+m);
for(int i=1,L=0,R=0;i<=m;i++)
{
int l=q[i].l,r=q[i].r;
while(L<l) del(a[L]),L++;
while(L>l) L--,ins(a[L]);
while(R>r) del(a[R]),R--;
while(R<r) R++,ins(a[R]);
ans[q[i].id].f=cnt;
ans[q[i].id].s=(r-l+1)*(r-l)/2;
if(!ans[q[i].id].f) ans[q[i].id].s=1;
else
{
int k=__gcd(ans[q[i].id].f,ans[q[i].id].s);
ans[q[i].id].f/=k,ans[q[i].id].s/=k;
}
}
for(int i=1;i<=m;i++)
printf("%d/%d\n",ans[i].f,ans[i].s);
return 0;
}
P3674 小清新人渣的本愿
前两个询问可以直接莫队+ bitset 解决,第三个直接枚举质因子在 bitset 里查有没有质因子即可,总复杂度是 \(\mathcal O(m(\sqrt{n}+\frac{n}{\omega}))\)。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,a[N],pos[N],cnt[N],res[N],B;
bitset<N> f,F;
bool ans[N];
struct Q
{
int l,r,op,id,x;
friend bool operator<(const Q &x,const Q &y)
{
if(pos[x.l]!=pos[y.l])
return pos[x.l]<pos[y.l];
if(pos[x.l]&1)
return x.r<y.r;
return x.r>y.r;
}
}q[N];
vector<Q> G[N];
void add(int x)
{
cnt[x]++;
if(cnt[x]==1) f[x]=1,F[100000-x]=1;
}
void del(int x)
{
cnt[x]--;
if(!cnt[x]) f[x]=0,F[100000-x]=0;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;B=sqrt(n);
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
pos[i]=(i-1)/B+1;
for(int i=1;i<=m;i++)
{
int op,l,r,x;
cin>>op>>l>>r>>x;
q[i]={l,r,op,i,x};
}
sort(q+1,q+1+m);
for(int i=1,L=0,R=0;i<=m;i++)
{
int l=q[i].l,r=q[i].r,id=q[i].id,x=q[i].x;
while(L<l) del(a[L]),L++;
while(L>l) L--,add(a[L]);
while(R<r) R++,add(a[R]);
while(R>r) del(a[R]),R--;
if(q[i].op==1) ans[id]=((f<<x)&f).any();
else if(q[i].op==2) ans[id]=((f<<(100000-x))&F).any();
else if(q[i].op==3)
{
ans[id]=false;
for(int j=1;j*j<=x;j++)
{
if(x%j) continue;
if(f[j]&&f[x/j])
{
ans[id]=true;
break;
}
}
}
}
for(int i=1;i<=m;i++)
{
if(ans[i]) cout<<"hana\n";
else cout<<"bi\n";
}
return 0;
}
SP10707 COT2 - Count on a tree II
这道题是树上莫队板子题,没啥好讲的。需要注意的是,如果采用的是欧拉序,那么一定要特别处理 LCA 处。因为 LCA 处在欧拉序区间中出现了两次,而我们把贡献给取消了!!
#include<bits/stdc++.h>
using namespace std;
const int N=100005,B=450;
int n,q,p[N<<1],a[N],f[N][20],st[N],ed[N],TI,dep[N],rnk[N<<1],cnt[N],num,ans[N];
bool flag[N];
struct Query
{
int l,r,id,L;
friend bool operator<(const Query &x,const Query &y)
{
if(p[x.l]!=p[y.l]) return p[x.l]<p[y.l];
if(p[x.l]&1) return x.r<y.r;
return x.r>y.r;
}
}Q[N];
vector<int> G[N],tmp;
void dfs(int x,int fa)
{
st[x]=++TI,f[x][0]=fa,dep[x]=dep[fa]+1,rnk[TI]=x;
for(int i=1;i<=18;i++)
f[x][i]=f[f[x][i-1]][i-1];
for(int v:G[x])
{
if(v==fa) continue;
dfs(v,x);
}
ed[x]=++TI,rnk[TI]=x;
}
int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=18;~i;i--)
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y) return x;
for(int i=18;~i;i--)
{
if(f[x][i]==f[y][i])
continue;
x=f[x][i],y=f[y][i];
}
return f[x][0];
}
void del(int x)
{
if(cnt[a[x]]==1) num--;
cnt[a[x]]--;
}
void ins(int x)
{
if(!cnt[a[x]]) num++;
cnt[a[x]]++;
}
void cal(int x)
{
if(!x) return;
flag[x]?del(x):ins(x);
flag[x]^=1;
}
int main()
{
cin>>n>>q;
for(int i=1;i<=n;i++)
{
cin>>a[i];
tmp.push_back(a[i]);
}
for(int i=1;i<=n*2;i++)
p[i]=(i-1)/B+1;
sort(tmp.begin(),tmp.end());
unique(tmp.begin(),tmp.end());
for(int i=1;i<=n;i++)
a[i]=lower_bound(tmp.begin(),tmp.end(),a[i])-tmp.begin()+1;
for(int i=1;i<n;i++)
{
int u,v;cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);
for(int i=1;i<=q;i++)
{
int x,y;cin>>x>>y;
if(st[x]>st[y]) swap(x,y);
int LC=LCA(x,y);
Q[i].id=i,Q[i].r=st[y];
if(LC==x) Q[i].l=st[x];
else Q[i].l=ed[x],Q[i].L=LC;
}
sort(Q+1,Q+1+q);
for(int i=1,L=0,R=0;i<=q;i++)
{
int l=Q[i].l,r=Q[i].r,id=Q[i].id;
while(L<l) cal(rnk[L]),L++;
while(L>l) L--,cal(rnk[L]);
while(R<r) R++,cal(rnk[R]);
while(R>r) cal(rnk[R]),R--;
if(Q[i].L) cal(Q[i].L);
ans[id]=num;
if(Q[i].L) cal(Q[i].L);
}
for(int i=1;i<=q;i++)
cout<<ans[i]<<"\n";
return 0;
}

浙公网安备 33010602011771号