每日打卡
目录
例题2:https://www.luogu.org/problemnew/show/P1972
例3:Codeforces Round #340 (Div. 2) E. XOR and Favorite Number
例题1
对于牛客暑期多校训练营第一场的J题,就是一个典型的莫队算法(但是被卡了),我也不知道这是什么玄学原理。
题目链接:https://www.nowcoder.com/acm/contest/139/J
题意:给n个数,q组询问,每组询问有l和r两个数,求a[1],a[2],...,a[l],a[r],...,a[n]中间有多少个不同的数。
题解:裸的莫队。
#include <bits/stdc++.h>
using namespace std;
#define INIT(x) memset(x,0,sizeof(x))
const int maxn = 100005;
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*10+ch-'0';ch=getchar();}
return x*f;
}
struct node{
int l,r,id;
}q[maxn];
int n,m,a[maxn],ans[maxn],num[maxn],tot;
int block;
bool cmp(const node &a,const node &b)
{
if(a.l/block!=b.l/block)
return a.l<b.l;
return a.r<b.r;
}
inline void add(int x)
{
num[a[x]]++;
if(num[a[x]]==1)
tot++;
}
inline void del(int x)
{
num[a[x]]--;
if(num[a[x]]==0)
tot--;
}
void init()
{
INIT(num);
INIT(a);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=0;i<n;i++)
a[i] = read();
block = sqrt(maxn);
for(int i=0;i<m;i++)
{
q[i].l = read()-1;
q[i].r = read()-1;
q[i].id = i;
}
sort(q,q+m,cmp);
int l=0,r=n-1; //注意这里,因为题目相当于挖去了中间区间,求其他区间不同的数个数,所以刚开始l和r的位置在一头一尾,并且它们(头尾)永远在最终要求的区间里,所以要先预处理
num[a[0]]++;
num[a[n-1]]++;
if(a[0]==a[n-1])
tot = 1;
else
tot = 2;
for(int i=0;i<m;i++)
{
while(l<q[i].l) //如果指针在区间左边,因为是求的外区间,所以就把这个数填上
add(++l);
while(l>q[i].l)
del(l--);
while(r<q[i].r)
del(r++);
while(r>q[i].r)
add(--r);
ans[q[i].id] = tot;
}
for(int i=0;i<m;i++)
printf("%d\n",ans[i]);
}
return 0;
}
例题2:https://www.luogu.org/problemnew/show/P1972
题意:和上题相反,此题给l和r区间,求a[l]到a[r]中有多少个不同的数。
题解:首先,弱数据可以用map过掉。但我们还是考虑用莫队来做这道题。由于题意的变化,所以我们需要变化初始指针的位置,让它均指向开头。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
#define INIT(x) memset(x,0,sizeof(x))
const int inf = 0x3f3f3f3f;
const int maxn = 1000005;
int a[maxn],n,m,block,ans[maxn],num[maxn],tot;
struct node{
int l,r,id;
bool operator<(const node&b)const{
if(l/block!=b.l/block)
return l<b.l;
return r<b.r;
}
}q[maxn];
inline void read(int &x)
{
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
inline void print(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)
print(x/10);
putchar(x%10+'0');
}
inline void add(int x)
{
num[a[x]]++;
if(num[a[x]]==1)
tot++;
}
inline void del(int x)
{
num[a[x]]--;
if(num[a[x]]==0)
tot--;
}
int main()
{
read(n);
block = sqrt(n);
for(int i=1;i<=n;i++)
read(a[i]);
read(m);
for(int i=0;i<m;i++)
{
read(q[i].l);
read(q[i].r);
q[i].id = i;
}
sort(q,q+m);
int l=0,r=0; //由于这次要求是在区间里面,所以刚开始两指针全都指向0,再依次向后移动
tot = 0;
for(int i=0;i<m;i++)
{
while(l<q[i].l) //如果指针在区间外面,则删掉区间外面的元素对应的num值,再把指针往里面挪
del(l++);
while(l>q[i].l)
add(--l);
while(r<q[i].r)
add(++r);
while(r>q[i].r)
del(r--);
ans[q[i].id] = tot;
}
for(int i=0;i<m;i++)
cout<<ans[i]<<endl;
return 0;
}
例3:Codeforces Round #340 (Div. 2) E. XOR and Favorite Number
题目链接:http://codeforces.com/contest/617/problem/E
题意:对于询问区间L和R,求有多少对l和r满足 a[l] xor a[l+1] xor.......xor a[r] = k。
题解:
首先可以求一下前缀和,pre[i]=a[1]^a[2]...^a[i];(pre[0]=0)
如果一个区间 a[l] xor a[l+1] xor.......xor a[r] = k , 那么就有pre[l-1]^pre[r] = a[l] xor a[l+1] xor.......xor a[r], 所求的问题便转化成了求一对l和r,使得pre[l-1]^pre[r]=k。我们依旧可以用莫队算法来解决这个问题。
当然了,本题有几个注意点:
- num数组记录的是pre[i]^k的结果的个数,即num[pre[l-1]^k]的个数等于num[pre[r]](若pre[l-1]^pre[r]=k) 的个数;
- num[0] = 1, 这是因为如果存在一个区间[0,0,0,,,k],它们的异或和为k,出现这种情况的话就会存在一种解法,所以num[0]=1;
- 开始指针l指向1,r指向1,是因为如果l指向0的话,则会出现l-1指向-1发生错误
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1<<20;
typedef long long ll;
ll n,m,k,block,num[maxn],tot,ans[maxn];
ll a[maxn];
struct node{
int l,r,id;
bool operator<(const node&b)const{
if(l/block!=b.l/block)
return l<b.l;
return r<b.r;
}
}q[maxn];
int main()
{
scanf("%lld%lld%lld",&n,&m,&k);
memset(num,0,sizeof(num));
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
a[i] ^= a[i-1];
}
block = sqrt(n);
for(int i=0;i<m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id = i;
}
sort(q,q+m);
tot = 0;
ll l=1,r=0;
num[0] = 1;
for(int i=0;i<m;i++)
{
while(l<q[i].l){
num[a[l-1]]--;
tot -= num[a[l-1]^k];
l++;
}
while(l>q[i].l){
l--;
tot += num[a[l-1]^k];
num[a[l-1]]++;
}
while(r<q[i].r){
r++;
tot += num[a[r]^k];
num[a[r]]++;
}
while(r>q[i].r){
num[a[r]]--;
tot -= num[a[r]^k];
r--;
}
ans[q[i].id] = tot;
}
for(int i=0;i<m;i++)
cout<<ans[i]<<endl;
return 0;
}