阅前声明
题单
为方便,题目链接均在洛谷。
要用桶的时候请尽量不要使用 map
或者 un_map
,会造成不必要的 TLE。
普通莫队
当一个区间问题,可以由 \([l,r]\) 转移到 \([l\pm 1,r]\) 和 \([l,r\pm 1]\),且添加、删除都可以已很快的时间完成时,我们可以使用莫队算法。
我们先将询问离线下来,并将他们排序。我们先把数列分为若干个块,块长为 \(S\),则先按 \(l\) 所在的区间排序,再按 \(r\) 的大小排序。
对于每一个询问,我们对于上个区间暴力转移到下个区间。可行转移方式之一是先扩大区间再缩小区间。
当 \(S=\sqrt{n}\) 时,时间复杂度为 \(n\sqrt{n}\)。
莫队的主体,按上面实现的话,应该是长这样:
int l=1,r=0;
For(i,1,m) {while(l>q[i].l) upd(a[--l]);while(r<q[i].r) upd(a[++r]);while(l<q[i].l) del(a[l++]);while(r>q[i].r) del(a[r--]);ans[q[i].idx]=sum;
}
DQUERY - D-query
题意:区间不同数
莫队主体是简单的,于是我们考虑加入和删除。
我们可以开一个桶保存每个数出现的次数:
- 加入时如果保存该数的桶为空,那么答案加 \(1\)。
- 删除后如果保存该数的桶为空,那么答案减 \(1\)。
- 然后对桶进行操作即可。
点击查看代码
#include<bits/stdc++.h>#define ll long long
#define i128 __int128#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()using namespace std;int read() {int x=0,f=1; char c=getchar();for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1); for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }#define maxn 200050int n,m,siz;
map<int,int> cnt;
int a[maxn];struct node{int l,r,idx;bool operator<(const node &x) {return l/siz==x.l/siz?r<x.r:l<x.l;}
}q[maxn];int sum=0;void upd(int x) {if(cnt[x]==0) sum++;cnt[x]++;
}void del(int x) {if(cnt[x]==1) sum--;cnt[x]--;
}int ans[maxn];void work() {in1(n);siz=sqrt(n);For(i,1,n) in1(a[i]);in1(m);For(i,1,m) in2(q[i].l,q[i].r),q[i].idx=i;sort(q+1,q+m+1);int l=q[1].l,r=q[1].l-1;For(i,1,m) {while(l>q[i].l) upd(a[--l]);while(r<q[i].r) upd(a[++r]);while(l<q[i].l) del(a[l++]);while(r>q[i].r) del(a[r--]);ans[q[i].idx]=sum;}For(i,1,m) cout<<ans[i]<<'\n';
}signed main() {
// ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);int _=1;
// _=read();For(i,1,_) {work();}return 0;
}
P1494 [国家集训队] 小 Z 的袜子
题意:求区间 \([l,r]\) 中随机选出两个数相等的概率。
简单计数题。
用一个桶 \(cnt\) 保存每一个数出现的次数,考虑一个数 \(x\) 的贡献。
- 加入 \(x\) 前,会对答案产生 \(cnt_x\) 的贡献。
- 删除 \(x\) 后,会对答案造成 \(-cnt_x\) 的贡献。
点击查看代码
#include<bits/stdc++.h>
#define int ll
#define ll long long
#define i128 __int128#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()using namespace std;int read() {int x=0,f=1; char c=getchar();for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1); for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }#define maxn 50040
int n,m,siz;
int a[maxn],cnt[maxn];
struct quest{int l,r,idx;
}q[maxn];
bool cmp(quest a,quest b) {return a.l/siz==b.l/siz?a.r<b.r:a.l<b.l;
}
pair<int,int> ans[maxn];int sum=0;
void upd(int x) {sum+=cnt[x]; cnt[x]++; }
void del(int x) {cnt[x]--; sum-=cnt[x]; }void work() {in2(n,m);For(i,1,n) in1(a[i]);For(i,1,m) in2(q[i].l,q[i].r),q[i].idx=i;siz=sqrt(n);sort(q+1,q+m+1,cmp);int l=q[1].l,r=q[1].l-1;For(i,1,m) {if(q[i].l==q[i].r) {ans[q[i].idx].first=0;ans[q[i].idx].second=1;continue ;}while(l>q[i].l) upd(a[--l]);while(r<q[i].r) upd(a[++r]);while(l<q[i].l) del(a[l++]);while(r>q[i].r) del(a[r--]);ans[q[i].idx].first=sum;ans[q[i].idx].second=(r-l+1)*(r-l)/2;if(!ans[q[i].idx].first) ans[q[i].idx].second=1;}For(i,1,m) {int G=__gcd(ans[i].first,ans[i].second);cout<<ans[i].first/G<<'/'<<ans[i].second/G<<'\n';}
}signed main() {
// ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);int _=1;
// _=read();For(i,1,_) {work();}return 0;
}
XOR and Favorite Number
询问 \([l,r]\) 中有多少个区间满足区间的异或和为 \(k\)。
首先我们知道 \(\oplus\) 是可以前缀和的。
然后求一段区间 \([l,r]\) 的异或和可以转换为前缀异或数组 \(s\) 的 \(s_{r}\oplus s_{l-1}\)。
所以答案变为求 \([l,r]\) 中 \(s_x \oplus s_y = k(x\le y)\) 的 \(\{x,y\}\) 对数。
我们用一个桶 \(cnt\) 来保存每个数的出现次数,那么:
- 加入 \(x\) 前,会对答案产生 \(cnt_{x\oplus k}\) 的贡献。
- 删除 \(x\) 后,会对答案产生 \(-cnt_{x\oplus k}\) 的贡献。
点击查看代码
#include<bits/stdc++.h>
#define int ll
#define ll long long
#define i128 __int128#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()using namespace std;int read() {int x=0,f=1; char c=getchar();for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1); for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }#define maxn 100050int n,m,k,siz;
int a[maxn];
int cnt[maxn*20];
ll sum=0;struct quest{int l,r,idx;bool operator<(const quest &x) const{return l/siz==(x.l/siz)?r<x.r:l<x.l;}
}q[maxn];void upd(int x) {sum+=cnt[x^k];cnt[x]++;
}void del(int x) {cnt[x]--;sum-=cnt[x^k];
}
int ans[maxn];void work() {in3(n,m,k);siz=sqrt(n);For(i,1,n) in1(a[i]);For(i,1,n) a[i]^=a[i-1];For(i,1,m) {in2(q[i].l,q[i].r);q[i].l--;//前缀和导致的q[i].idx=i;}sort(q+1,q+m+1);int l=1,r=0;For(i,1,m) {while(l>q[i].l) upd(a[--l]);while(r<q[i].r) upd(a[++r]);while(l<q[i].l) del(a[l++]);while(r>q[i].r) del(a[r--]);ans[q[i].idx]=sum;}For(i,1,m) cout<<ans[i]<<'\n';
}signed main() {
// ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);int _=1;
// _=read();For(i,1,_) {work();}return 0;
}