莫队算法 第一次接触到这个题目应该是黑龙江省赛 和东北赛 当时想着一定要学会这个算法 结果拖了四个月
今天开始学莫队算法
首先 莫队算法是为了解决离线查询的题目 我们知道 很多区间问题线段树可以实现 但是让我们头疼的是一些信息线段树很快实现
这时候我们就需要用到莫队算法 莫队算法的好处就是他首先一个区间【L,R】 可以用【L,R+1】,【L-1,R】【L+1,R】【L,R-1】
O(1)知道 那么你要求的区间不过就是 区间的曼哈顿距离?可以建曼哈顿最小距离树
但是我们为啥不用最小曼哈顿距离树的思想(取最近的8*n)放在区间上 按照查询的分块排序后 可以实现最小的曼哈顿距离
时间复杂度 是 O(n^1.5)
模板
//如果程序可以从[L,R+1],[L-1,R],[L+1,R],[L,R-1]得到 可以用莫队
//注意先扩张后收缩
//莫队算法主要用来离线查询
ll a[MAX_N],pos[MAX_N],ans[MAX_N];
struct node {int l,r,id;}Q[MAX_N];
bool cmp(node a,node b){
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l]<pos[b.l];
}
int L = 0,R = 0;//多组要重置
ll Ans = 0;
void add(int x);
void del(int x);
int main(){
scanf("%d%d%d",&n,&m,&k);
int sz = sqrt(n);
for(int i = 1;i<=n;++i){
scanf("%lld",&a[i]);
pos[i] = i/sz;
}
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,cmp);
for(int i = 1;i<=m;++i){
while(R<Q[i].r){
R++;
add(R);
}
while(L>Q[i].l){
L--;
add(L);
}
while(L<Q[i].l){
del(L);
L++;
}
while(R>Q[i].r){
del(R);
R--;
}
ans[Q[i].id] = Ans;
}
for(int i = 1;i<=m;++i)
printf("%lld\n",ans[i]);
return 0;
}
莫队第一题 Codeforces 617 E
题意 要你求一个区间内有多少连续异或的数等于k
我们考虑异或的性质 前缀和异或 i - j 就是j - i-1前缀和
并且观察L R 容易从 四个区间推过来 就可以上莫队了
Trick 1 异或加起来可能会很大
flag[0] = 1是初始条件 因为不取就是 1 了
代码如下
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
const int MAX_N = 1<<20;
int n,m,k;
ll a[MAX_N],flag[MAX_N],pos[MAX_N],ans[MAX_N];
ll Ans=0;
int L = 1,R = 0;
void add(int x){
Ans+=flag[a[x]^k];
flag[a[x]]++;
}
void del(int x){
flag[a[x]]--;
Ans-=flag[a[x]^k];
}
struct node{
int l,r,id;
}Q[MAX_N];
bool cmp(node a,node b){
if(pos[a.l] ==pos[b.l]) return a.r<b.r;
return pos[a.l]<pos[b.l];
}
int main(){
scanf("%d%d%d",&n,&m,&k);
int blo = sqrt(n);
for(int i = 1;i<=n;++i){
scanf("%lld",&a[i]);
a[i]^=a[i-1];
pos[i] = i/blo;
}
for(int i = 1;i<=m;++i){
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id = i;
}
flag[0] = 1;
sort(Q+1,Q+1+m,cmp);
for(int i = 1;i<=m;++i){
while(L<Q[i].l){
del(L-1);
L++;
}
while(L>Q[i].l){
L--;
add(L-1);
}
while(R<Q[i].r){
R++;
add(R);
}
while(R>Q[i].r){
del(R);
R--;
}
ans[Q[i].id] = Ans;
}
for(int i = 1;i<=m;++i)
printf("%lld\n",ans[i]);
return 0;
}
莫队第二题 BZOJ 2038 小Z的袜子
这题我们不难知道分母就是C(2,len) 那么我们可以推一下组合式子就知道化简一下可得add和del的贡献就是加和减flag[a[x]]
#include<stdio.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e5+5;
struct data
{
int l,r,id;
}Q[maxn];
int pos[maxn];
int a[maxn];
bool cmp(const data &a,const data &b)
{
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l]<pos[b.l];
}
long long ans[maxn];
long long ans2[maxn];
int flag[maxn];
int L=1,R=0;//由于第一个删除0的操作对答案有影响,所以可以直接L=1开始。
long long Ans=0;
long long gcd_(long long a,long long b)
{
return b==0?a:gcd_(b,a%b);
}
void add(int x)//先统计再加
{
Ans+=flag[a[x]];
flag[a[x]]++;
}
void del(int x)//先减再统计
{
flag[a[x]]--;
Ans-=flag[a[x]];
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
int sz=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[i]=i/sz;
}
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,cmp);
for(int i=1;i<=m;i++)
{
while(R<Q[i].r)
{
R++;
add(R);
}
while(L>Q[i].l)
{
L--;
add(L);
}
while(L<Q[i].l)
{
del(L);
L++;
}
while(R>Q[i].r)
{
del(R);
R--;
}
ans[Q[i].id]=Ans;
ans2[Q[i].id]=(1LL*(Q[i].r-Q[i].l+1)*(Q[i].r-Q[i].l))/2;
long long tmp = gcd_(ans[Q[i].id],ans2[Q[i].id]);
ans[Q[i].id]/=tmp;
ans2[Q[i].id]/=tmp;
if(ans[Q[i].id]==0) ans2[Q[i].id]=1;
}
for(int i=1;i<=m;i++)
printf("%lld/%lld\n",ans[i],ans2[i]);
return 0;
}
题意 问你区间内有多少不同的数字
算是模板题了 只要flag[a[x]]等于1并且相减 那么答案要减去1 如果flag[a[x]]等于0并且相加 那么答案要加上1
/*
DQUERY - D-query
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAX_N = 1000024;
typedef long long ll;
int pos[MAX_N],flag[MAX_N],a[MAX_N];
ll ans[MAX_N];
struct node{int l,r,id;}Q[MAX_N];
ll Ans = 0;
int L = 1,R = 0;
bool cmp(const node &a,const node &b){
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l]<pos[b.l];
}
void add(int x){
if(!flag[a[x]]){
Ans++;
}
flag[a[x]]++;
}
void del(int x){
if(flag[a[x]]==1){
Ans--;
}
flag[a[x]]--;
}
int main(){
int n,m;
scanf("%d",&n);
int blo = sqrt(n);
for(int i = 1;i<=n;++i){
scanf("%d",&a[i]);
pos[i] = i/blo;
}
scanf("%d",&m);
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,cmp);
for(int i = 1;i<=m;++i){
while(R<Q[i].r){
R++;
add(R);
}
while(L>Q[i].l){
L--;
add(L);
}
while(L<Q[i].l){
del(L);
L++;
}
while(R>Q[i].r){
del(R);
R--;
}
ans[Q[i].id] = Ans;
}
for(int i = 1;i<=m;++i)
printf("%lld\n",ans[i]);
return 0;
}
第四题 Codeforeces 86 D
题意就是一段区间内的价值为一个数出现的次数的平方乘上他自己 我们就可以很简单的根据莫队来推add 和 del了
没错 叫平方差公式
/*
codeforces 86 D
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
using namespace std;
const int MAX_N = 1000024;
typedef long long ll;
int a[MAX_N],pos[MAX_N];
ll flag[MAX_N],ans[MAX_N];
ll Ans = 0;
int L = 1,R = 0;
struct node{int l,r,id;}Q[MAX_N];
bool cmp(const node &a,const node &b){
if(pos[a.l]==pos[b.l]) return a.r< b.r;
return pos[a.l]<pos[b.l];
}
void add(int x){
Ans+=a[x]*(flag[a[x]]*2+1);
flag[a[x]]++;
}
void del(int x){
Ans-=a[x]*((flag[a[x]]-1)*2+1);
flag[a[x]]--;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
int blo = sqrt(n);
for(int i = 1;i<=n;++i){
scanf("%d",&a[i]);
pos[i] = i/blo;
}
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,cmp);
for(int i = 1;i<=m;++i){
while(R<Q[i].r){
R++;
add(R);
}
while(L>Q[i].l){
L--;
add(L);
}
while(L<Q[i].l){
del(L);
L++;
}
while(R>Q[i].r){
del(R);
R--;
}
ans[Q[i].id] = Ans;
}
for(int i = 1;i<=m;++i)
printf("%I64d\n",ans[i]);
return 0;
}
第五题 HDOJ 5213 Lucky
题意是给你好多数 然后给你两个区间 一左一右 你分别从区间内取一个数 加起来等于k 这两个区间我们该如何操作呢?
其实不难想到利用容斥 进行容斥容易明白[L1,R1] + [L2,R2]的答案为[L1 , R2 ]- [L1 - L2-1]-[L2 , R2-1] + [R1+1,L2-1]
便可以操作了
这是第一个版本
/*
hdoj 5213 Lucky
*/
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <sstream>
#define dbg(x) ;
//cout<<#x<<" = "<< (x)<< endl
using namespace std;
const int MAX_N = 2e5+5;
typedef long long ll;
int a[MAX_N],pos[MAX_N];
ll ans[MAX_N],flag[MAX_N],flag_l[MAX_N],flag_r[MAX_N],flag_m[MAX_N];
ll Ans= 0,Ans_l = 0,Ans_r = 0,Ans_m=0;
int n,k,m;
int L = 1,R = 0,L_l = 1,R_l = 0,L_r = 1,R_r = 0,L_m = 1,R_m = 0;
void add(int x){
if(k>a[x]&&k-a[x]<n)
Ans+=flag[k-a[x]];
flag[a[x]]++;
}
void del(int x){
flag[a[x]]--;
if(a[x]<k&&k-a[x]<=n)
Ans-=flag[k-a[x]];
}
void add_l(int x){
if(k>a[x]&&k-a[x]<=n)
Ans_l+=flag_l[k-a[x]];
flag_l[a[x]]++;
}
void del_l(int x){
flag_l[a[x]]--;
if(a[x]<k&&k-a[x]<=n)
Ans_l-=flag_l[k-a[x]];
}
void add_r(int x){
if(k>a[x]&&k-a[x]<=n)
Ans_r+=flag_r[k-a[x]];
flag_r[a[x]]++;
}
void del_r(int x){
flag_r[a[x]]--;
if(a[x]<k&&k-a[x]<=n)
Ans_r-=flag_r[k-a[x]];
}
void add_m(int x){
if(k>a[x]&&k-a[x]<=n)
Ans_m+=flag_m[k-a[x]];
flag_m[a[x]]++;
}
void del_m(int x){
flag_m[a[x]]--;
if(a[x]<k&&k-a[x]<=n)
Ans_m-=flag_m[k-a[x]];
}
ll ans1[MAX_N],ans2[MAX_N],ans3[MAX_N],ans4[MAX_N];
struct node {int l,r,id;}Q1[MAX_N],Q2[MAX_N],Q3[MAX_N],Q4[MAX_N],Q[MAX_N],T[MAX_N];
bool cmp(const node &a,const node &b){
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l]<pos[b.l];
}
int main(){
while(scanf("%d",&n)!=EOF){
L = 1,R = 0,L_l = 1,R_l = 0,L_r = 1,R_r = 0,L_m = 1,R_m = 0;
Ans= 0,Ans_l = 0,Ans_r = 0,Ans_m=0;
for(int i = 1;i<=n;++i){
flag_l[i] = flag_m[i] = flag_r[i] = flag[i] = 0;
}
scanf("%d",&k);
int blo = sqrt(n);
for(int i = 1;i<=n;++i){
scanf("%d",&a[i]);
pos[i] = i/blo;
}
scanf("%d",&m);
for(int i = 1;i<=m;++i){
scanf("%d%d%d%d",&Q[i].l,&Q[i].r,&T[i].l,&T[i].r);
Q1[i].l = Q[i].l,Q1[i].r = T[i].r,Q1[i].id = i;
Q2[i].l = Q[i].l,Q2[i].r = T[i].l-1,Q2[i].id = i;
Q3[i].l = Q[i].r+1,Q3[i].r = T[i].r,Q3[i].id = i;
Q4[i].l = Q[i].r+1,Q4[i].r = T[i].l-1,Q4[i].id = i;
}
sort(Q1+1,Q1+1+m,cmp);
sort(Q2+1,Q2+1+m,cmp);
sort(Q3+1,Q3+1+m,cmp);
sort(Q4+1,Q4+1+m,cmp);
for(int i = 1;i<=m;++i){
while(R<Q1[i].r){
R++;
add(R);
}
while(L>Q1[i].l){
L--;
add(L);
}
while(L<Q1[i].l){
del(L);
L++;
}
while(R>Q1[i].r){
del(R);
R--;
}
ans1[Q1[i].id] = Ans;
dbg(Ans);
while(R_l<Q2[i].r){
R_l++;
add_l(R_l);
}
while(L_l>Q2[i].l){
L_l--;
add_l(L_l);
}
while(L_l<Q2[i].l){
del_l(L_l);
L_l++;
}
while(R_l>Q2[i].r){
del_l(R_l);
R_l--;
}
ans2[Q2[i].id] = Ans_l;
dbg(Ans_l);
while(R_r<Q3[i].r){
R_r++;
add_r(R_r);
}
while(L_r>Q3[i].l){
L_r--;
add_r(L_r);
}
while(L_r<Q3[i].l){
del_r(L_r);
L_r++;
}
while(R_r>Q3[i].r){
del_r(R_r);
R_r--;
}
ans3[Q3[i].id] = Ans_r;
dbg(Ans_r);
while(R_m<Q4[i].r){
R_m++;
add_m(R_m);
}
while(L_m>Q4[i].l){
L_m--;
add_m(L_m);
}
while(L_m<Q4[i].l){
del_m(L_m);
L_m++;
}
while(R_m>Q4[i].r){
del_m(R_m);
R_m--;
}
ans4[Q4[i].id] = Ans_m;
}
for(int i = 1;i<=m;++i){
printf("%lld\n",ans1[i]-ans2[i]-ans3[i]+ans4[i]);
}
}
return 0;
}
这是修改后的第二个版本
/*
HDOJ 5213 LUCKY new
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define dbg(x) ;
//cout<<#x<<" = "<< (x)<< endl
using namespace std;
int n,m,k;
const int MAX_N = 2e5+5;
typedef long long ll;
int pos[MAX_N],a[MAX_N];
ll ans[4][MAX_N],flag[4][MAX_N],Ans[4];
struct node {int l,r,id,belong;}Q[MAX_N<<1];
bool cmp(const node &a,const node &b){
if(pos[b.l]==pos[a.l]) return a.r<b.r;
return pos[a.l] < pos[b.l];
}
int L[4],R[4];
void add(int x,int p){
if(a[x]<k&&k-a[x]<=n)
Ans[p]+=flag[p][k-a[x]];
flag[p][a[x]]++;
}
void del(int x,int p){
if(a[x]<k&&k-a[x]<=n)
Ans[p]-=flag[p][k-a[x]];
flag[p][a[x]]--;
}
int main(){
while(scanf("%d%d",&n,&k)!=EOF){
int blo = sqrt(n);
for(int i = 0;i<4;++i){
Ans[i] = 0;
L[i] = 1;
R[i] = 0;
}
for(int i = 1;i<=n;++i){
scanf("%d",&a[i]);
pos[i] = i/blo;
for(int j = 0;j<4;++j)
flag[j][i] = 0;
}
scanf("%d",&m);
int cnt = 1;
for(int i = 1;i<=m;++i){
int l1,l2,r1,r2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
Q[cnt].l = l1,Q[cnt].r = r2,Q[cnt].id = i,Q[cnt++].belong = 0;
Q[cnt].l = l1,Q[cnt].r = l2-1,Q[cnt].id = i,Q[cnt++].belong = 1;
Q[cnt].l = r1+1,Q[cnt].r = r2,Q[cnt].id = i,Q[cnt++].belong = 2;
Q[cnt].l = r1+1,Q[cnt].r = l2-1,Q[cnt].id = i,Q[cnt++].belong = 3;
}
cnt--;
sort(Q+1,Q+1+cnt,cmp);
for(int i = 1;i<=cnt;i++){
while(R[Q[i].belong]<Q[i].r){
R[Q[i].belong]++;
add(R[Q[i].belong],Q[i].belong);
}
while(L[Q[i].belong]>Q[i].l){
L[Q[i].belong]--;
add(L[Q[i].belong],Q[i].belong);
}
while(L[Q[i].belong]<Q[i].l){
del(L[Q[i].belong],Q[i].belong);
L[Q[i].belong]++;
}
while(R[Q[i].belong]>Q[i].r){
del(R[Q[i].belong],Q[i].belong);
R[Q[i].belong]--;
}
dbg(Q[i].belong);
ans[Q[i].belong][Q[i].id] = Ans[Q[i].belong];
}
for(int i = 1;i<=m;++i){
dbg(ans[0][i]);
dbg(ans[1][i]);
dbg(ans[2][i]);
dbg(ans[3][i]);
printf("%lld\n",ans[0][i]-ans[1][i]-ans[2][i]+ans[3][i]);
}
}
return 0;
}
莫队第六题 FZU 2226 信心题
这题的题意是给你N个数 问你 L-R个区间一样的数距离最大的距离是多少
我们可以用vector存坐标 在读入的时候直接放进vector
然后用莫队开两个数组维护左下标 与右下标
注意右下标初始化要为-1 作下标为0 防止设为0 0 的时候第一个数R++
左右下标就变成 0 1有两个下标至少有两个数 这是不正确的 因为我们假设只通过一个数
为什么只动r呢 因为这题l r r在l右边才是合法状态 看代码吧
/*
FZU 2226
*/
#include <cstdio>
#include <cstring>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
const int MAX_N = 500024;
typedef long long ll;
int a[MAX_N],pos[MAX_N],cnt_l[1024],cnt_r[1024];
ll ans[MAX_N];
int L,R;
vector<int > vt[1024];
struct node {
int l,r,id;
}Q[MAX_N];
ll Ans;
bool cmp(const node &a,const node &b){
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l]<pos[b.l];
}
int main(){
int n,m;
while(~scanf("%d",&n)){
for(int i = 1;i<=1000;++i){
cnt_l[i] = 0;
cnt_r[i] = 0;
vt[i].clear();
}
L=1,R=0,Ans = 0;
int blo = sqrt(n);
for(int i = 1;i<=n;++i){
scanf("%d",&a[i]);
vt[a[i]].push_back(i);
pos[i] = i / blo;
}
scanf("%d",&m);
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,cmp);
for(int i= 1;i<=m;++i){
while(R<Q[i].r){
R++;
cnt_r[a[R]]++;
}
while(L>Q[i].l){
L--;
cnt_l[a[L]]--;
}
while(L<Q[i].l){
cnt_l[a[L]]++;
L++;
}
while(R>Q[i].r){
cnt_r[a[R]]--;
R--;
}
int maxx =0;
for(int j = 1;j<=1000;j++){
if(cnt_r[j]<1) continue;
dbg(cnt_l[j]);
int tmp1 = vt[j][cnt_r[j]-1];
int tmp2 = vt[j][cnt_l[j]];
maxx = max(maxx,tmp1-tmp2);
}
ans[Q[i].id] = maxx;
}
for(int i = 1;i<=m;++i)
printf("%lld\n",ans[i]);
}
return 0;
}
莫队第七题 莫队第七题Groups
考虑添加数和删除数判断左右 具体看代码吧
/*
hdoj 4638
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
#define dbg(x) ;
//cout<<#x<<" = "<< (x)<< endl
typedef long long ll;
const int MAX_N = 2000024;
int a[MAX_N],pos[MAX_N],flag[MAX_N];
struct node {
int l,r,id;
}Q[MAX_N];
ll ans[MAX_N];
bool cmp(const node &a,const node &b){
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l] < pos[b.l];
}
int L = 1,R = 0;
ll Ans = 0;
void add(int x){
if((!flag[a[x]-1])&&(!flag[a[x]+1])) Ans++;
else if((flag[a[x]-1])&&(flag[a[x]+1])) Ans--;
flag[a[x]]++;
}
void del(int x){
if((!flag[a[x]-1])&&(!flag[a[x]+1])) Ans--;
else if((flag[a[x]-1])&&(flag[a[x]+1])) Ans++;
flag[a[x]]--;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
int blo = sqrt(n);
L = 1,R = 0,Ans = 0;
for(int i = 1;i<=n;++i){
scanf("%d",&a[i]);
pos[i] = i/blo;
flag[i] = 0;
}
flag[n+1] = 0;
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,cmp);
for(int i = 1;i<=m;++i){
while(R<Q[i].r){
R++;
add(R);
dbg(Ans);
}
while(L>Q[i].l){
L--;
add(L);
}
while(L<Q[i].l){
del(L);
L++;
}
while(R>Q[i].r){
del(R);
R--;
}
dbg(Ans);
ans[Q[i].id] = Ans;
}
for(int i = 1;i<=m;++i)
printf("%lld\n",ans[i]);
}
return 0;
}
莫队第八题 HDOJ 4676
这题我们得知道一个公式
对一个序列的某个区间L,R,每个数两两之间的GCD之和为
Σd|nφ(d)×C(2,num(d)); d在这里指这个区间的所有约数
知道公式就直接写 不知道公式。。。
/*
hdoj 4676
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
const int MAX_N = 1e5+5;
struct node{
int l,r,id;
}Q[MAX_N];
int pos[MAX_N];
int phi[MAX_N];
int pri[MAX_N];
int a[MAX_N];
int ans[MAX_N];
int Flag[MAX_N];
vector<int > v[MAX_N];
int num[MAX_N];
bool cmp(const node &a,const node &b){
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l]<pos[b.l];
}
void Getphi(int Max){
phi[1] = 1;
for(int i = 2;i<=Max;++i)
{
if(!Flag[i]){
phi[i] = i-1;
pri[++pri[0]] = i;
}
for(int j = 1;j<=pri[0];j++){
if(1LL*i*pri[j]>Max) break;
Flag[i*pri[j]] = 1;
if(i%pri[j] == 0){
phi[i*pri[j]] = phi[i]*pri[j];
break;
}
phi[i*pri[j]] = phi[i] *(pri[j] - 1);
}
}
for(int i = 1;i<=Max;++i)
{
for(int j = i;j<=Max;j+=i)
v[j].push_back(i);
}
}
int L = 0,R = 0;
long long Ans = 0;
void add(int x){
for(int i = 0;i<v[a[x]].size();i++)
Ans+=1ll*phi[v[a[x]][i]]*num[v[a[x]][i]];
for(int i = 0;i<v[a[x]].size();i++)
num[v[a[x]][i]]++;
return ;
}
void del(int x){
for(int i = 0;i<v[a[x]].size();i++)
num[v[a[x]][i]]--;
for(int i = 0;i<v[a[x]].size();i++)
Ans-=1LL*phi[v[a[x]][i]]*num[v[a[x]][i]];
return ;
}
int main(){
int t;
Getphi(20000);
scanf("%d",&t);
for(int l = 1;l<=t;++l){
printf("Case #%d:\n",l);
int n;
L = 0,R = 0,Ans = 0;
scanf("%d",&n);
for(int i = 1;i<=n;++i) num[i] = 0;
int blo = sqrt(n);
for(int i = 1;i<=n;++i){
scanf("%d",&a[i]);
pos[i] = i / blo;
}
int T;
scanf("%d",&T);
for(int i = 1;i<=T;++i){
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id = i;
}
sort(Q+1,Q+1+T,cmp);
for(int i= 1;i<=T;i++){
while(R<Q[i].r){
R++;
add(R);
}
while(L>Q[i].l){
L--;
add(L);
}
while(L<Q[i].l){
del(L);
L++;
}
while(R>Q[i].r){
del(R);
R--;
}
ans[Q[i].id] = Ans;
}
for(int i = 1;i<=T;++i)
printf("%d\n",ans[i]);
}
return 0;
}
莫队第九题 NBUT 1457
用带修莫队加立方和公式解决这个问题
/*
NBUT 1457
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <sstream>
#include <cmath>
using namespace std;
const int MAX_N = 100024;
int a[MAX_N],b[MAX_N],pos[MAX_N];
struct node {int l,r,id;}Q[MAX_N];
long long flag[MAX_N];
bool cmp(const node &a,const node &b){
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l] < pos[b.l];
}
int L = 1,R = 0;
long long Ans,ans[MAX_N];
void add(int x){
Ans-=1LL*flag[a[x]]*flag[a[x]]*flag[a[x]];
flag[a[x]]++;
Ans+=1LL*flag[a[x]]*flag[a[x]]*flag[a[x]];
}
void del(int x){
Ans-=1LL*flag[a[x]]*flag[a[x]]*flag[a[x]];
flag[a[x]]--;
Ans+=1LL*flag[a[x]]*flag[a[x]]*flag[a[x]];
}
int main(){
int n;
while(scanf("%d",&n)==1){
int blo = (int)sqrt(n*1.0);
for(int i = 0;i<=100000;i++)
flag[i] = 0;
L = 1,R = 0,Ans = 0;
for(int i = 1;i<=n;++i){
scanf("%d",&a[i]);
pos[i]=i /blo;
b[i] = a[i];
}
sort(b+1,b+1+n);
int num = unique(b+1,b+1+n) - b- 1;
for(int i = 1;i<=n;++i)
a[i] = lower_bound(b+1,b+1+n,a[i]) - b- 1;
int T;
scanf("%d",&T);
for(int i = 1;i<=T;++i){
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id = i;
}
sort(Q+1,Q+1+T,cmp);
for(int i = 1;i<=T;++i){
while(R<Q[i].r){
R++;
add(R);
}
while(L>Q[i].l){
L--;
add(L);
}
while(L<Q[i].l){
del(L);
L++;
}
while(R>Q[i].r){
del(R);
R--;
}
ans[Q[i].id] = Ans;
}
for(int i = 1;i<=T;++i)
printf("%I64d\n",ans[i]);
}
return 0;
}
莫队第十题 cf 940 F
这题题意就是让你找区间内数的出现次数 从小到大第一个没有的数
其实可以暴力找范围 不然维护很麻烦
/*
cf 940F
*/
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
using namespace std;
const int MAX_N = 300024;
int pos[MAX_N],col[MAX_N],b[MAX_N],ans[MAX_N],Num[MAX_N],flag[MAX_N],lst[MAX_N];
struct node {int l,r,pos,k;}q[MAX_N];
struct opti {int x,y,z;}o[MAX_N];
int L = 1,R = 0,Ans = 0,K = 0,q_num=0,o_num;
bool cmp(const node &a,const node &b){
if(pos[a.l]^pos[b.l]) return pos[a.l]<pos[b.l];
if(pos[a.r]^pos[b.r]) return pos[a.r]<pos[b.r];
return a.k<b.k;
}
void add(int x){
Num[flag[col[x]]]--;
Num[++flag[col[x]]]++;
}
void del(int x){
Num[flag[col[x]]]--;
Num[--flag[col[x]]]++;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
int blo = pow(n,2.0/3);
for(int i = 1;i<=n;++i){
scanf("%d",&col[i]);
pos[i] = i / blo;
b[i] = col[i];
lst[i] = col[i];
}
for(int i = 1;i<=m;++i){
int opt,x,y;
scanf("%d%d%d",&opt,&x,&y);
if(opt==1){
q[++q_num].l = x,q[q_num].r= y,q[q_num].pos = q_num,q[q_num].k = o_num;
}
else {
o[++o_num].x = x,o[o_num].y = y,o[o_num].z = lst[x],lst[x] = y,b[n+o_num] = y;
}
}
sort(b+1,b+1+n+o_num);
int b_num = unique(b+1,b+1+n+o_num)-b-1;
sort(q+1,q+1+q_num,cmp);
for(int i = 1;i<=n;i++){
col[i] = lower_bound(b+1,b+1+b_num,col[i]) - b;
//lst[i] = lower_bound(b+1,b+1+b_num,lst[i]) - b;
}
for(int i = 1;i<=o_num;i++){
o[i].z = lower_bound(b+1,b+1+b_num,o[i].z)-b;
o[i].y = lower_bound(b+1,b+1+b_num,o[i].y)- b;
}
for(int i = 1;i<=q_num;i++){
while(K<q[i].k){
++K;
if(o[K].x<=R&&o[K].x>=L){
Num[flag[col[o[K].x]]]--;
Num[--flag[col[o[K].x]]]++;
Num[flag[o[K].y]]--;
Num[++flag[o[K].y]]++;
}
col[o[K].x] = o[K].y;
}
while(K>q[i].k){
if(o[K].x<=R&&o[K].x>=L){
Num[flag[col[o[K].x]]]--;
Num[--flag[col[o[K].x]]]++;
Num[flag[o[K].z]]--;
Num[++flag[o[K].z]]++;
}
col[o[K].x] = o[K].z;
--K;
}
while(R<q[i].r){
R++;
add(R);
}
while(L>q[i].l){
L--;
add(L);
}
while(L<q[i].l){
del(L);
L++;
}
while(R>q[i].r){
del(R);
R--;
}
for(int i = 1;i<=2000000;i++)
if(!Num[i]) {
Ans = i;
break;
}
ans[q[i].pos] = Ans;
}
for(int i = 1;i<=q_num;++i)
printf("%d\n",ans[i]);
return 0;
}
莫队第十一题 CF 220B
题意维护区间内出现一个数的出现个数为他本身个数有多少个 还是比较好维护的
/*
cf 220B
*/
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
using namespace std;
#define dbg(x) ;
//cout<<#x<<" = "<< (x)<< endl
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*10+ch-'0';ch=getchar();}
return x*f;
}
const int MAX_N = 1000024;
int a[MAX_N],b[MAX_N],ans[MAX_N],pos[MAX_N],flag[MAX_N];
struct node{int l,r,id;}Q[MAX_N];
int L = 1,R = 0,Ans;
bool cmp (const node &a, const node &b){
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l] <pos[b.l];
}
void add(int x){
if(flag[a[x]]==a[x]){
Ans--;
}
flag[a[x]]++;
if(flag[a[x]]==a[x]){
Ans++;
}
}
void del(int x){
if(flag[a[x]]==a[x]){
Ans--;
}
flag[a[x]]--;
if(flag[a[x]]==a[x]){
Ans++;
}
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
int blo = sqrt(n);
for(int i = 1;i<=n;++i){
a[i] = read();
if(a[i]>=100001) a[i] = 100001;
pos[i] = i/blo;
}
for(int i = 1;i<=m;++i){
Q[i].l = read();
Q[i].r = read();
Q[i].id = i;
}
sort(Q+1,Q+1+m,cmp);
for(int i = 1;i<=m;++i){
while(R<Q[i].r){
R++;
add(R);
}
while(L>Q[i].l){
L--;
add(L);
}
while(L<Q[i].l){
del(L);
L++;
}
while(R>Q[i].r){
del(R);
R--;
}
ans[Q[i].id] = Ans;
}
for(int i = 1;i<=m;++i)
printf("%d\n",ans[i]);
return 0;
}
莫队第十二题 UVA 12345
想让你实现一个类集合的查找不同元素个数
可以修改 用带修莫队维护
/*
UVA 12345
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <sstream>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
const int MAX_N = 1000024;
int ans[MAX_N],col[MAX_N],lst[MAX_N],pos[MAX_N],flag[MAX_N];
int L = 1,R = 0,Ans = 0,K = 0,q_num,o_num;
struct node {int l ,r, id,k;}q[MAX_N];
struct opti {int l,r,z;}o[MAX_N];
bool cmp (const node &a,const node &b){
if(pos[a.l]^pos[b.l]) return pos[a.l] < pos[b.l];
if(pos[a.r]^pos[b.r]) return pos[a.r]<pos[b.r];
return a.k<b.k;
}
void add(int x){
if(!flag[col[x]]) Ans++;
flag[col[x]]++;
}
void del(int x){
if(flag[col[x]]==1) Ans--;
flag[col[x]]--;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
int blo = pow(n,2.0/3);
for(int i = 1;i<=n;++i){
scanf("%d",&col[i]);
pos[i] = i/blo;
lst[i] = col[i];
}
for(int i = 1;i<=m;++i){
char str[5];
int x,y;
scanf("%s%d%d",str,&x,&y);
x++;
if(str[0]=='Q'){
q_num++;
q[q_num].l = x,q[q_num].r = y,q[q_num].id = q_num,q[q_num].k = o_num;
}
else {
o_num++;
o[o_num].l = x,o[o_num].r = y,o[o_num].z = lst[x],lst[x] = y;
}
}
sort(q+1,q+1+q_num,cmp);
for(int i = 1;i<=q_num;++i){
while(K<q[i].k){
++K;
if(o[K].l>=L&&o[K].l<=R){
if(!--flag[col[o[K].l]]) Ans--;
if(!flag[o[K].r]++) Ans++;
}
col[o[K].l] = o[K].r;
}
while(K>q[i].k){
if(o[K].l>=L&&o[K].l<=R){
if(!--flag[col[o[K].l]]) Ans--;
if(!flag[o[K].z]++) Ans++;
}
col[o[K].l] = o[K].z;
--K;
}
while(R<q[i].r){
R++;
add(R);
}
while(L>q[i].l){
L--;
add(L);
}
while(L<q[i].l){
del(L);
L++;
}
while(R>q[i].r){
del(R);
R--;
}
ans[q[i].id] = Ans;
}
for(int i = 1;i<=q_num;++i)
printf("%d\n",ans[i]);
return 0;
}
莫队第13题 BZOJ 2120
写了这题学会了带修莫队 放最后让你们开心下 哈哈
/*
BZOJ 2120
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
const int MAX_N = 50024;
int ans[MAX_N],flag[1000024],col[MAX_N],pos[MAX_N],lst[MAX_N];
struct node {int l,r,id,pos,k;}Q[MAX_N];
struct opti {int l,r,z;}o[MAX_N];
int L = 1 ,R = 0,Ans = 0,K = 0,q_num,o_num;
bool cmp (const node &a,const node &b){
if(pos[a.l]^pos[b.l]) return pos[a.l]<pos[b.l];
if(pos[a.r]^pos[b.r]) return pos[a.r]<pos[b.r];
return a.k<b.k;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
int blo = pow(n,2.0/3);
for(int i = 1;i<=n;++i){
scanf("%d",&col[i]);
pos[i] = i / blo;
lst[i] = col[i];
}
char str[20];
int x,y;
for(int i = 1;i<=m;++i){
scanf("%s%d%d",str,&x,&y);
if(str[0]!='R'){
Q[++q_num].l = x,Q[q_num].r = y,Q[q_num].pos = q_num,Q[q_num].k = o_num;
}
else {
o[++o_num].l = x,o[o_num].r = y,o[o_num].z = lst[x],lst[x] = y;
}
}
sort(Q+1,Q+1+q_num,cmp);
for(int i = 1;i<=q_num;i++){
while(K<Q[i].k){
++K;
if(L<=o[K].l&&o[K].l<=R){
if(!--flag[col[o[K].l]]) Ans--;
if(!flag[o[K].r]++) Ans++;
}
col[o[K].l] = o[K].r;
}
while(K>Q[i].k){
if(L<=o[K].l&&o[K].l<=R){
if(!--flag[col[o[K].l]]) Ans--;
if(!flag[o[K].z]++) Ans++;
}
col[o[K].l] = o[K].z;
--K;
}
while(R<Q[i].r){
R++;
if(!flag[col[R]]++) Ans++;
}
while(L>Q[i].l){
L--;
if(!flag[col[L]]++) Ans++;
}
while(L<Q[i].l){
if(!--flag[col[L]]) Ans--;
L++;
}
while(R>Q[i].r){
if(!--flag[col[R]]) Ans--;
R--;
}
ans[Q[i].pos] = Ans;
}
for(int i = 1;i<=q_num;++i)
printf("%d\n",ans[i]);
return 0;
}
未完待续