莫队

莫队是个维护区间操作的好东西,它采用分块的思想,每次暴力跳区间,使得指针移动次数为 \(\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\),那么所求即为:

\[\sum\limits_{i=1}^x \frac{a_i(a_i-1)}{2} \]

考虑怎么 \(\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;
}
posted @ 2025-06-08 17:08  I_AK_CTSC  阅读(28)  评论(0)    收藏  举报