%队:忧压的暴力



莫队一般分为以下几大类:

  • 普通莫队

用于不带修改的离线查询,区间信息 \(O(1)\)\(O(能过)\) 扩展.

  • 带修莫队

用于允许离线的带修改区间查询.

  • 树上莫队

把树的欧拉序或dfs序跑出来,然后在跑出的序列上莫队.
是用于树上的莫队(废话

  • 回滚莫队

分为只增和只减两种,用于增加或删除操作中有一个特别难搞但另一个比较滞胀的情况.
然而我暂时不会

另外还有Tricks或能组合使用的套路,这里就把它们单独列出来:

  • 值域分块

顾名思义,对值域进行分块,求答案的时候通过值域上的跳跃进行求解.
其实当初自己看的时候根本不能顾名思义
大概是因为之前做那道Rmq Problem / Mex的时候还没学分块,直接看了值域分块
毕竟二次离线对莫队入门者极其友好,看都不敢看

  • 二次离线

哦,不会(
貌似是lxl毒瘤搞出来的套路?


莫队的时间复杂度主要取决于以下两个操作:

  • 1.分块

分块的大小直接决定了主程序在跑暴力的途中得到的优化程度.
普通莫队一般取\(\sqrt{n}\),带修看情况取\(\sqrt[3]{nt}\)(理论最优)或\(n^{\frac{2}{3}}\).

  • 2.排序

一般采用左端点块号优先,右端点编号其次.

inline bool cmp(query a,query b)
{
	return (bel[a.l]^bel[b.l])?bel[a.l]

这时普通莫队的复杂度基本已经达到了\(O(n\sqrt{n})\).
然而还有一种玄学优化,奇偶性排序

inline bool cmp(query a,query b)
{
	return (bel[a.l]^bel[b.l])?bel[a.l]b.r);
}

对于奇数块按第二关键字升序排序,偶数块降序排序,这样跑完奇数块上升区又可以顺便下坡跑完偶数块,玄学分析上可以减少一半的运行时间


以下为题目code:

1.P3901 数列找不同

拿来练手的()

#include 
#define ri register int
#define MAXN 1000001
#define G() getchar()
using namespace std;
template inline void r(T &x)
{
	x=0;
	char i=G();
	bool f=1;
	while(!isdigit(i))
	f&=(i!='-'),i=G();
	while(isdigit(i))
	x=(x<<3)+(x<<1)+i-'0',i=G();
	x*=((f<<1)-1);
}
int n,q,bel[MAXN],totb,lenb;
int cnt[MAXN],a[MAXN];
int cl,cr;
int temp;
bool ans[MAXN];
struct query{
	int l,r;
	int ind;
}qr[MAXN];
bool cmp(query a,query b)
{
	return (bel[a.l]^bel[b.l])?bel[a.l]b.r);
}
inline void add(int pos)
{
	if((++cnt[a[pos]])==1)
	temp++;
}
inline void del(int pos)
{
	if(!(--cnt[a[pos]]))
	temp--;
}
int main()
{
	r(n),r(q);
	lenb=sqrt(n);
    totb=ceil((double)n/lenb);
    for(ri i=1;i<=totb;i++)
    for(ri j=(i-1)*lenb+1;j<=i*lenb;j++)
    bel[j]=i;
    for(ri i=1;i<=n;i++)
    r(a[i]);
    for(ri i=1;i<=q;i++)
    {
        qr[i].ind=i;
        r(qr[i].l);
        r(qr[i].r);
    }
    sort(qr+1,qr+q+1,cmp);
    ri tl,tr;
    for(ri i=1;i<=q;i++)
    {
        tl=qr[i].l,tr=qr[i].r;
        while(cltl)
        add(--cl);
        while(crtr)
        del(cr--);
        if(temp==tr-tl+1)
        ans[qr[i].ind]=1;
    }
    for(ri i=1;i<=q;i++)
    printf("%s\n",ans[i]?"Yes":"No");
    return 0;
}

2.P2709 小B的询问

稍微推一下柿子就好了,把完全平方拆开,其实是个裸的莫队()

#include 
#define ri register int
#define MAXN 100001
using namespace std;

#define G() getchar()
template inline void r(T &x)
{
	x=0;
	char in=G();
	bool f=1;
	while(!isdigit(in))
	f&=(in!='-'),in=G();
	while(isdigit(in))
	x=(x<<3)+(x<<1)+in-'0',in=G();
	x*=((f<<1)-1);
}

int n,m,k;
int cl=1,cr;
int a[MAXN];
int ans[MAXN];
int bel[MAXN];
int cnt[MAXN];
int temp;
int totb,lenb;

struct query{
	int l,r;
	int ind;
}qr[MAXN];

inline bool cmp(query a,query b)
{
	return (bel[a.l]^bel[b.l])?bel[a.l]b.r);
}

inline void add(int x)
{
	temp+=(1+2*cnt[a[x]]);
	cnt[a[x]]++;
}

inline void del(int x)
{
	temp-=(2*cnt[a[x]]-1);
	cnt[a[x]]--;
}

int main()
{
	r(n),r(m);
	r(k);
	lenb=sqrt(n);
	totb=ceil((double)n/lenb);
	for(ri i=1;i<=totb;i++)
	for(ri j=(i-1)*lenb+1;j<=i*lenb;j++)
	bel[j]=i;
	for(ri i=1;i<=n;i++)
	r(a[i]);
	for(ri i=1;i<=m;i++)
	{
		qr[i].ind=i;
		r(qr[i].l),r(qr[i].r);
	}
	sort(qr+1,qr+m+1,cmp);
	for(ri i=1;i<=m;i++)
	{
		int L=qr[i].l,R=qr[i].r;
		while(clL)
		add(--cl);
		while(crR)
		del(cr--);
		ans[qr[i].ind]=temp;
	}
	for(ri i=1;i<=m;i++)
	printf("%d\n",ans[i]);
	return 0;
}

3.P1494 小Z的袜子

推柿子,然后发现这也是个裸题 概率在统计的时候算贡献可以降次(什么奇妙说法),所以这题也裸了

哦对了,这题还教会我一个迭代式的gcd(a,b):

inline int gcd(int a,int b)
{
	if(!b)
	return a;
	while(b^=a^=b^=a%=b);
	return a;
}
#include 
#define ri register int
#define MAXN 1000001
#define int long long
#define g() getchar()
using namespace std;

int n,m;
int cl=1,cr=0;
int totb,lenb;
int a[MAXN];
int ans1[MAXN],ans2[MAXN];
int temp1,temp2;
int cnt[MAXN];
int bel[MAXN];

inline int gcd(int a,int b)
{
	if(!b)
	return a;
	while(b^=a^=b^=a%=b);
	return a;
}

template inline void r(T &x)
{
	x=0;
	char in=g();
	bool f=1;
	while(!isdigit(in))
	f&=(in!='-'),in=g();
	while(isdigit(in))
	x=(x<<3)+(x<<1)+in-'0',in=g();
	x*=((f<<1)-1);
}

struct query{
	int l,r;
	int ind;
}qr[MAXN];

inline bool cmp(query a,query b)
{
	return (bel[a.l]^bel[b.l])?bel[a.l]b.r);
}

inline void add(int x)
{
	//temp2+=(len<<1);
	temp1+=(cnt[a[x]]<<1);
	cnt[a[x]]++;
}

inline void del(int x)
{
	//temp2+=(2-(len<<1));
	temp1+=(2-(cnt[a[x]]<<1));
	cnt[a[x]]--;
}

inline void calc(int a,int b,int ind)
{
	if(a==0)
	b=1,ans1[ind]=0,ans2[ind]=1;
	else
	{
		int temp=gcd(a,b);
//		cout<<"a="<L)
		add(--cl);
		while(crR)
		del(cr--);
		if(L==R)
		calc(0,1,qr[i].ind);
		else
		calc(temp1,(cr-cl+1)*(cr-cl),qr[i].ind);
	}
	for(ri i=1;i<=m;i++)
	printf("%d/%d\n",ans1[i],ans2[i]);
	return 0;
}

4.T213452 Epsilon in SwI

跟上一题可以说是一模一样,只需要稍微推一下积分展开就行了
完全没有难度

另外,这题说是要离散化,但是写的随机数据太拉了不离散化甚至更快()

哎,麻乐,还是推下柿子罢

\[\frac{2}{r^2-l^2}\epsilon(l,r)\int_l^r(\sum_{i=1}^{maxa}cnt[i]^2x)dx \]

这一大坨看起来很恐怖,但是只要你懂一点点定积分你就会看出:

\[\int_l^r(\sum_{i=1}^{maxa}cnt[i]^2x)dx=\frac{\sum_{i=1}^{maxa}cnt[i]^2}{2}(r^2-l^2) \]

然后出题人很贴心地把系数整了个

\[\frac{2}{r^2-l^2} \]

那这不就裸了吗?
所以答案是:

\[\epsilon(l,r)\sum_{i=1}^{maxa}cnt[i]^2 \]

,这时候你会发现这其实是一道\(\texttt{小Z的妹子}\),于是就裸了。

//Epsilon in SwI  
//std code  
//by Neutralized.
//2021.11.28 11:13

#include
#define ri register int
#define int long long
#define MAXN 1000001
#define g() getchar()
using namespace std;

int n,m;
int a[MAXN],b[MAXN];
int cnt[MAXN];
int ans1[MAXN],ans2[MAXN];
int bel[MAXN];
int totb,lenb;
int temp1,temp2;

template inline void r(T &x)
{
	x=0;
	char in=g();
	bool f=1;
	while(!isdigit(in))
	f&=(in!='-'),in=g();
	while(isdigit(in))
	x=(x<<3)+(x<<1)+(in^48),in=g();
	x*=((f<<1)-1);
}

inline int gcd(int a,int b)
{
	if(!b)
	return a;
	while(b^=a^=b^=a%=b);
	return a;
}

struct query{
	int l,r;
	int ind;
}qr[MAXN];

inline bool cmp(query a,query b)
{
	return (bel[a.l]^bel[b.l])?bel[a.l]b.r);
}

inline void add(int x)
{
	temp1+=3*cnt[a[x]]*cnt[a[x]]-3*cnt[a[x]];
	temp2+=(cnt[a[x]]<<1)+1;
	cnt[a[x]]++;
}

inline void del(int x)
{
	temp1+=9*cnt[a[x]]-6-3*cnt[a[x]]*cnt[a[x]];
	temp2+=1-(cnt[a[x]]<<1);
	cnt[a[x]]--;
}

inline void calc(int a,int b,int ind)
{
	if(!a)
	ans1[ind]=0,ans2[ind]=1;
	else
	{
		int temp=gcd(a,b);
		a/=temp,b/=temp;
		ans1[ind]=a,ans2[ind]=b;
	}
}

signed main()
{
	ri cl=1,cr=0;
	ri lef,rig;
	r(n),r(m);
	lenb=sqrt(n);
	totb=ceil((double)n/lenb);
	for(ri i=1;i<=totb;i++)
	for(ri j=(i-1)*lenb+1;j<=i*lenb;j++)
	bel[j]=i;
	for(ri i=1;i<=n;i++)
	r(a[i]),b[i]=a[i];
	sort(b+1,b+n+1);
	ri len=unique(b+1,b+n+1)-b-1;
	for(ri i=1;i<=n;i++)
	a[i]=lower_bound(b+1,b+len+1,a[i])-b;
	for(ri i=1;i<=m;i++)
	r(qr[i].l),r(qr[i].r),qr[i].ind=i;
	sort(qr+1,qr+m+1,cmp);
	for(ri i=1;i<=m;i++)
	{
		lef=qr[i].l,rig=qr[i].r;
		while(cllef)
		add(--cl);
		while(crrig)
		del(cr--);
		if(lef==rig)
		calc(0,1,qr[i].ind);
		else
		calc(temp1*temp2,(cr-cl+1)*(cr-cl)*(cr-cl-1),qr[i].ind);
	}
	for(ri i=1;i<=m;i++)
	printf("%lld/%lld\n",ans1[i],ans2[i]);
	return 0;
} 

5.P3709 大爷的字符串题

  • 区间众数球阀:
    num[x]表示出现了x次的数个数,cnt[x]表示x的出现次数
    莫队转移的时候注意一下就行了.

这题卡了我1h 因为有一行忘打了,囸

#include 
#define ri register int
#define int long long
#define MAXN 1000001
using namespace std;

#define G() getchar()
template inline void r(T &x)
{
	x=0;
	char in=G();
	bool f=1;
	while(!isdigit(in))
	f&=(in!='-'),in=G();
	while(isdigit(in))
	x=(x<<3)+(x<<1)+in-'0',in=G();
	x*=((f<<1)-1);
}

int n,m,k;
int cl=1,cr;
int a[MAXN];
int b[MAXN];
int ans[MAXN];
int bel[MAXN];
int cnt[MAXN];
int num[MAXN];
int temp;
int totb,lenb;

struct query{
	int l,r;
	int ind;
}qr[MAXN];

inline bool cmp(query a,query b)
{
	return (bel[a.l]^bel[b.l])?bel[a.l]b.r);
}

inline void add(int x)
{
	num[cnt[a[x]]]--;
	num[++cnt[a[x]]]++;
	temp=max(temp,cnt[a[x]]);
}

inline void del(int x)
{
	//unfinished
	//10 pts
	num[cnt[a[x]]]--;
	if(cnt[a[x]]==temp&&!num[cnt[a[x]]])
	temp--;
	num[--cnt[a[x]]]++; //我日 加这一行就过了 
}

signed main()
{
	r(n),r(m);
	lenb=sqrt(n);
	totb=ceil((double)n/lenb);
	for(ri i=1;i<=totb;i++)
	for(ri j=(i-1)*lenb+1;j<=i*lenb;j++)
	bel[j]=i;
	for(ri i=1;i<=n;i++)
	r(a[i]),b[i]=a[i];
	sort(b+1,b+n+1);
	ri len=unique(b+1,b+n+1)-b-1;
	for(ri i=1;i<=n;i++)
	a[i]=lower_bound(b+1,b+len+1,a[i])-b;
	for(ri i=1;i<=m;i++)
	{
		qr[i].ind=i;
		r(qr[i].l),r(qr[i].r);
	}
	sort(qr+1,qr+m+1,cmp);
	for(ri i=1;i<=m;i++)
	{
		int L=qr[i].l,R=qr[i].r;
		while(clL)
		add(--cl);
		while(crR)
		del(cr--);
		ans[qr[i].ind]=temp;
	}
	for(ri i=1;i<=m;i++)
	printf("%lld\n",-ans[i]);
	return 0;
}

6.P4137 Rmq Problem / mex

这题引入了一个新知识:

\[\color{cornflowerblue}{\small\texttt{Range Partitioning}} \]

\[\color{cornflowerblue}{\texttt{值域分块}} \]

所谓值域分块,就是把出现在题目值域中的值进行分块处理,可以帮你把暴力卡过去 可以实现某些点快得飞起()
具体见代码

#include 
#define ri register int
#define MAXN 300001
#define g() getchar()
#define pc(a) putchar(a)
#define Tp template
using namespace std;
const int inf=0x3f3f3f3f;
const int len=448;

namespace SlowIO
{
	Tp inline void rd(T &x)	{
		x=0;char i=g();bool f=1;
		while(!isdigit(i)) f&=(i!='-'),i=g();
		while(isdigit(i)) x=(x<<3)+(x<<1)+(i^48),i=g();
		x*=((f<<1)-1);
		}
	Tp inline void op(T x){
		if(x<0) pc('-'),x=-x;
		if(x>=10) op(x/10);
		pc(x%10+48);
		}
	Tp inline void writeln(T x){op(x);pc('\n');}
	Tp inline void writesp(T x){op(x);pc(' ');}
	Tp inline void writespf(T x){pc(' ');op(x);}
};
using namespace SlowIO;

int n,m;
int cl,cr;
int a[MAXN];
int num[MAXN];
int cnt[MAXN];
int L[MAXN],R[MAXN];
int ans[MAXN];
int bel[MAXN];

struct query{
    int l,r;
    int ind;
}qr[MAXN];

inline bool cmp(query a,query b)
{
    return (bel[a.l]^bel[b.l])?bel[a.l]b.r);
}

inline void initt()
{
    //值域分块
    for(ri i=1;i<=2e5;i++)
    bel[i]=(i-1)/len+1,L[i]=(i-1)*len+1,R[i]=i*len;
}

inline void add(int x)
{
    if(!cnt[a[x]])
    num[bel[a[x]]]++;
    cnt[a[x]]++;
}

inline void del(int x)
{
    if(cnt[a[x]]==1)
    num[bel[a[x]]]--;
    cnt[a[x]]--;
}

inline int gtans()
{
    if(!cnt[0])
    return 0;
    int ind=1;
    while(num[ind]==len) ind++;
    for(ri i=L[ind];i<=R[ind];i++)
    if(!cnt[i]) return i;
}
#define VSC Shimokitazawa
int main()
{
    rd(n),rd(m);
    initt();
    for(ri i=1;i<=n;i++)
    rd(a[i]);
    for(ri i=1;i<=m;i++)
    rd(qr[i].l),rd(qr[i].r),qr[i].ind=i;
    sort(qr+1,qr+m+1,cmp);
    // cl=qr[1].l+1,cr=qr[1].l;
    cl=1,cr=0;
    //test!
    for(ri i=1;i<=m;i++)
    {
        int tl=qr[i].l,tr=qr[i].r;
        while(cltl)
        add(--cl);
        while(crtr)
        del(cr--);
        ans[qr[i].ind]=gtans();
    }
    for(ri i=1;i<=m;i++)
    writeln(ans[i]);
    #ifdef VSC
    system("pause");
    #endif
    return 0;
}

7.P4396 [AHOI2013]作业

这题需要一个旧知识:

\[\color{cornflowerblue}{\small\texttt{Range Partitioning}} \]

\[\color{cornflowerblue}{\texttt{值域分块}} \]

草,针灸蒯过来不准备删呗

对于task 1task 2分别处理即可
注意统计的时候可以搞一点加速,帮你踩一堆人

#include 
#define ri register int
#define MAXN 300001
#define g() getchar()
#define pc(a) putchar(a)
#define Tp template
using namespace std;
const int inf=0x3f3f3f3f;
const int len=317;

namespace SlowIO
{
	Tp inline void rd(T &x)	{
		x=0;char i=g();bool f=1;
		while(!isdigit(i)) f&=(i!='-'),i=g();
		while(isdigit(i)) x=(x<<3)+(x<<1)+(i^48),i=g();
		x*=((f<<1)-1);
		}
	Tp inline void op(T x){
		if(x<0) pc('-'),x=-x;
		if(x>=10) op(x/10);
		pc(x%10+48);
		}
	Tp inline void writeln(T x){op(x);pc('\n');}
	Tp inline void writesp(T x){op(x);pc(' ');}
	Tp inline void writespf(T x){pc(' ');op(x);}
};
using namespace SlowIO;

struct query{
    int l,r;
    int a,b;
    int ind;
}qr[MAXN];

int n,m;
int cl,cr;
int a[MAXN];
int cnt[MAXN];
int num[MAXN];
int sum[MAXN];
int L[MAXN],R[MAXN],bel[MAXN];
pair ans[MAXN];

inline bool cmp(query a,query b)
{
    return (bel[a.l]^bel[b.l])?bel[a.l]b.r);
}

inline void initt()
{
    //值域分块
    for(ri i=1;i<=1e5;i++)
    bel[i]=(i-1)/len+1,L[i]=(i-1)*len+1,R[i]=i*len;
}

inline void add(int x)
{
    if(!cnt[a[x]])
    num[bel[a[x]]]++;
    cnt[a[x]]++;
    sum[bel[a[x]]]++;
}

inline void del(int x)
{
    if(cnt[a[x]]==1)
    num[bel[a[x]]]--;
    cnt[a[x]]--;
    sum[bel[a[x]]]--;
}

inline pair gtans(int a,int b)
{
    int ans1=0,ans2=0;
    if(bel[a]==bel[b])
    {
        for(ri i=a;i<=b;i++)
            if(cnt[i]>0) ans2++,ans1+=cnt[i];
    }
    else
    {
        int ind=1,temp;//这里写复杂了一点,可以去看文件夹里的小优化版本,会快22ms
        while(L[ind]b) break;
            ans1+=sum[temp],ans2+=num[temp];
        }
        temp--;
        for(ri i=a;i0) ans2++,ans1+=cnt[i];
        for(ri i=R[temp]+1;i<=b;i++)//这里记得R[temp]+1,因为R[temp]在距离b最近的上一块里(temp)已经计算过了,但不加你谷水数据还能放过去66pts
        if(cnt[i]>0) ans2++,ans1+=cnt[i];
    }
    return make_pair(ans1,ans2);
}
#define VSC Shimokitazawa
int main()
{
	rd(n),rd(m);
    initt();
	for(ri i=1;i<=n;i++)
	rd(a[i]);
    for(ri i=1;i<=m;i++)
    {
        rd(qr[i].l),rd(qr[i].r);
        rd(qr[i].a),rd(qr[i].b);
        qr[i].ind=i;
    }
    sort(qr+1,qr+m+1,cmp);
    cl=1;
    for(ri i=1;i<=m;i++)
    {
        int tl=qr[i].l,tr=qr[i].r;
        while(cl>tl)
        add(--cl);
        while(cltr)
        del(cr--);
        ans[qr[i].ind]=gtans(qr[i].a,qr[i].b);
    }
    for(ri i=1;i<=m;i++)
    writesp(ans[i].first),writeln(ans[i].second);
    #ifdef VSC
    system("pause");
    #endif
	return 0;
}
//好耶

8.P3730 曼哈顿交易

这题还是值域分块.
这里问的是求静态区间第k小,而且tag也有莫队,所以用莫队+值域分块解决.
需要离散化.

#include 
#define ri register int
#define MAXN 1000001
#define g() getchar()
#define pc(a) putchar(a)
#define Tp template
using namespace std;
const int inf=0x3f3f3f3f;
const int len=317;

namespace SlowIO
{
	Tp void rd(T &x)	{
		x=0;char i=g();bool f=1;
		while(!isdigit(i)) f&=(i!='-'),i=g();
		while(isdigit(i)) x=(x<<3)+(x<<1)+(i^48),i=g();
		x*=((f<<1)-1);
	}
	Tp inline void op(T x){
		if(x<0) pc('-'),x=-x;
		if(x>=10) op(x/10);
		pc(x%10+48);
	}
	Tp inline void writeln(T x){op(x);pc('\n');}
	Tp void writesp(T x){op(x);pc(' ');}
};
using namespace SlowIO;

int n,m;
int b[MAXN];
int a[MAXN];
int cnt[MAXN],num[MAXN];
int tot[MAXN];
int bel[MAXN],L[MAXN],R[MAXN];
int cl=1,cr;
int tl,tr;
int temp;
int ans[MAXN];

struct query{
	int l,r;
	int k;
	int ind;
}qr[MAXN];

inline bool cmp(query a,query b)
{
    return (bel[a.l]^bel[b.l])?bel[a.l]b.r);
}

inline void add(int x)
{
    if(!cnt[a[x]])
    temp++;
    else
    {
        num[bel[cnt[a[x]]]]--;
        tot[cnt[a[x]]]--;
    }
    cnt[a[x]]++;
    num[bel[cnt[a[x]]]]++;
    tot[cnt[a[x]]]++;
}

inline void del(int x)
{
    tot[cnt[a[x]]]--;
    num[bel[cnt[a[x]]]]--;
    cnt[a[x]]--;
    if(!cnt[a[x]])
    temp--;
    else
    tot[cnt[a[x]]]++,num[bel[cnt[a[x]]]]++;
}

inline void initt()
{
    //range partitioning
    for(ri i=1;i<=1e5;i++)
    bel[i]=(i-1)/len+1,L[i]=(i-1)*len+1,R[i]=i*len;
}

inline int gtans(int k)
{
    if(temp0) k-=num[top++];
    for(ri i=L[top];i<=R[top];i++)
    {
        if(tot[i]>0) k-=tot[i];
        if(k<=0)
        return i;
    }
}

int main()
{
    rd(n),rd(m);
    initt();
    for(ri i=1;i<=n;i++)
    rd(a[i]),b[i]=a[i];
    sort(b+1,b+n+1);
    int lent=unique(b+1,b+n+1)-b-1;
    for(ri i=1;i<=n;i++)
    a[i]=lower_bound(b+1,b+lent+1,a[i])-b;
    for(ri i=1;i<=m;i++)
    rd(qr[i].l),rd(qr[i].r),rd(qr[i].k),qr[i].ind=i;
    sort(qr+1,qr+m+1,cmp);
    for(ri i=1;i<=m;i++)
    {
        tl=qr[i].l,tr=qr[i].r;
        while(cl>tl)
        add(--cl);
        while(cltr)
        del(cr--);
        ans[qr[i].ind]=gtans(qr[i].k);
    }
    for(ri i=1;i<=m;i++)
    writeln(ans[i]);
    return 0;
}

9.P4867 Gty的二逼妹子序列

一看,值域上的询问
所以直接上值域分块,然后膜队
设计莫队的转移尽量使每次转移复杂度小,最后算答案稍微复杂一点不会有太大影响

#include 
#define ri register int
#define MAXN 1000001
#define g() getchar()
#define pc(a) putchar(a)
#define Tp template
using namespace std;
const int inf=0x3f3f3f3f;
const int len=317;

namespace SlowIO
{
	Tp inline void rd(T &x)	{
		x=0;char i=g();bool f=1;
		while(!isdigit(i)) f&=(i!='-'),i=g();
		while(isdigit(i)) x=(x<<3)+(x<<1)+(i^48),i=g();
		x*=((f<<1)-1);
	}
	Tp inline void op(T x){
		if(x<0) pc('-'),x=-x;
		if(x>=10) op(x/10);
		pc(x%10+48);
	}
	Tp inline void writeln(T x){op(x);pc('\n');}
	Tp inline void writesp(T x){op(x);pc(' ');}
};
using namespace SlowIO;

int n,m;
int cl=1,cr;
int a[MAXN];
int num[MAXN],cnt[MAXN];
int ans[MAXN];

#define bel(a) (a-1)/len+1
#define L(a) (a-1)*len+1
#define R(a) (a*len)

struct query{
	int l,r;
	int a,b;
	int ind;
}qr[MAXN];

inline bool cmp(query a,query b){
	return (bel(a.l)^bel(b.l))?bel(a.l)b.r);
}

inline void add(int x)
{
	if(!cnt[a[x]])
	num[bel(a[x])]++;
	cnt[a[x]]++;
}

inline void del(int x)
{
	if(cnt[a[x]]==1)
	num[bel(a[x])]--;
	cnt[a[x]]--;
}

int gtans(int a,int b)
{
	int ans1=0;
	if(bel(a)==bel(b))
	{
		for(ri i=a;i<=b;i++)
		if(cnt[i]>0) ans1++;
	}
	else
	{
		int ind=(L(bel(a))==a?bel(a):bel(a)+1),temp;
		for(temp=ind;true;temp++)
		if(R(temp)>b) break;
		else ans1+=num[temp];
		temp--;
		for(ri i=a;i0) ans1++;
		for(ri i=R(temp)+1;i<=b;i++)
		if(cnt[i]>0) ans1++;
	}
	return ans1;
}

int main()
{
	rd(n),rd(m);
	for(ri i=1;i<=n;i++)
	rd(a[i]);
	for(ri i=1;i<=m;i++)
	rd(qr[i].l),rd(qr[i].r),rd(qr[i].a),rd(qr[i].b),qr[i].ind=i;
	sort(qr+1,qr+m+1,cmp);
	static int tl,tr;
	for(ri i=1;i<=m;i++)
	{
		tl=qr[i].l,tr=qr[i].r;
		while(cl>tl)
		add(--cl);
		while(cltr)
		del(cr--);
		ans[qr[i].ind]=gtans(qr[i].a,qr[i].b);
	}
	for(ri i=1;i<=m;i++)
	writeln(ans[i]);
    return 0;
}

10.P4462 异或序列

摘自Strange_S的题解:

首先异或和满足前缀,也就是说设sum[i]为a[1]^a[2]^...^a[i] ,那么a[i]^a[i+1]^...^a[j]=sum[j]^sum[i-1]
而且异或不仅满足交换律,而且对于a^b=c时,a^c=b,b^c=a这两个式子同样成立
那么就好做了,假设当前i到j这个子串的异或和为k,就说明sum[j]^sum[i-1]=k,也就是sum[i-1]^k=sum[j],sum[j]^k=sum[i-1]
然后在区间转移的时候,设cnt[i]为当前区间值为i的前缀有多少个,然后对于增加序列长度的操作,假设新加的位置为r+1,我们先将cnt[sum[r+1]]++,然后求出ans+=cnt[sum[r+1]^k],左边扩展也是如此,不过注意,向左扩展时,对ans的更新是用sum[l-1]的,因为是sum[j]与sum[i-1]可以满足前缀
而且向右扩展的时候,如果sum[r+1]^k=sum[l-1]的话,ans++,因为我们更新的时候没有计算[l...r+1]区间的影响,所以要维护一下
而对于区间缩小的情况,就ans先减,再更新cnt,因为要先消除贡献再减cnt,其它步骤类似就好了

一句话,利用异或的前缀性和自反性,可以导出转移.
因为年代久远忘了所以直接看code

#include 
#define ri register int
#define MAXN 1000001
#define g() getchar()
#define pc(a) putchar(a)
#define Tp template
using namespace std;
const int inf=0x3f3f3f3f;
int len;

namespace SlowIO
{
	Tp inline void rd(T &x)	{
		x=0;char i=g();bool f=1;
		while(!isdigit(i)) f&=(i!='-'),i=g();
		while(isdigit(i)) x=(x<<3)+(x<<1)+(i^48),i=g();
		x*=((f<<1)-1);
	}
	Tp inline void op(T x){
		if(x<0) pc('-'),x=-x;
		if(x>=10) op(x/10);
		pc(x%10+48);
	}
	Tp inline void writeln(T x){op(x);pc('\n');}
	Tp inline void writesp(T x){op(x);pc(' ');}
};
using namespace SlowIO;

int n,m,k;
int totb;
int a[MAXN];
int pxor[MAXN];
int cnt[MAXN];
int bel[MAXN];
int cl,cr;
int temp;
int ans[MAXN];

struct query{
	int l,r;
	int k;
	int ind;
}qr[MAXN];

inline bool cmp(query a,query b)
{
    return (bel[a.l]^bel[b.l])?bel[a.l]b.r);
}

inline void add(int x)
{
    temp+=cnt[pxor[x]^k];
    cnt[pxor[x]]++;
}

inline void del(int x)
{
    cnt[pxor[x]]--;
    temp-=cnt[pxor[x]^k];
}

inline void initt()
{
    //pre
    len=sqrt(n);
    totb=ceil((double)n/len);
    for(ri i=1;i<=totb;i++)
    for(ri j=(i-1)*len+1;j<=i*len;j++)
    bel[j]=i;
}

int main()
{
    ri tl,tr;
    rd(n),rd(m),rd(k);
    initt();
    cnt[0]=1;
    for(ri i=1;i<=n;i++)
    rd(a[i]),pxor[i]=(pxor[i-1]^a[i]);
    for(ri i=1;i<=m;i++)
    rd(qr[i].l),qr[i].l--,rd(qr[i].r),qr[i].ind=i;
    //在[l,r]上使用前缀XOR应该包含[l-1,r]这一段
    //XOR(i=l -> r) ai=XOR(i=1 -> l-1) ai (xor) XOR(i=1 -> r) ai
    sort(qr+1,qr+m+1,cmp);
    for(ri i=1;i<=m;i++)
    {
        tl=qr[i].l,tr=qr[i].r;
        while(cl>tl)
        add(--cl);
        while(cltr)
        del(cr--);
        ans[qr[i].ind]=temp;
    }
    for(ri i=1;i<=m;i++)
    writeln(ans[i]);
    return 0;
}

\[\color{cornflowerblue}{\huge\texttt{to be continued......}} \]