https://vjudge.net/group/guati可以去这里找我挂的题,第二第三道要硬拿莫队做,有点难,不建议写。
以下为学习记录:
http://codeforces.com/contest/617/problem/E
昨天补了一道非常接近莫队思维的题
可以看看我这篇博客E题然后再来看莫队就好理解了:
https://blog.csdn.net/weixin_43262291/article/details/90271693
(之前贴错链接了。。。已更)
- 普通莫队
莫队其实就是一个暴力的算法,它适用于询问m个区间查询,对于每一个查询没有强制在线并且每个l-r可以O1的转换到l-1-r,l+1-r,l-r-1,l-r+1。
例题:cf 617E:http://codeforces.com/contest/617/problem/E
题解:
A. n的1e5和m的1e5通过莫队来实现
B. 前缀异或
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int maxn = 1e5+5;
int a[maxn],pos[maxn],cnt[1<<21];
ll ans[maxn];
struct node{
int l,r,id;
}q[maxn];
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(){
IO;
int n,m,k;cin>>n>>m>>k;
int z = sqrt(n);
for1(i,n){
cin>>a[i];
a[i]^=a[i-1];
pos[i] = i/z;
}
forn(i,m){
cin>>q[i].l>>q[i].r;
q[i].id = i;
}
sort(q,q+m,cmp);
int l=1,r = 0;
cnt[0] = 1;
ll res = 0;
forn(i,m){
while(r<q[i].r){
r++;
res+=cnt[a[r]^k];
cnt[a[r]]++;
}
while(l>q[i].l){
l--;
res+=cnt[a[l-1]^k];
cnt[a[l-1]]++;
}
while(r>q[i].r){
cnt[a[r]]--;
res-=cnt[a[r]^k];
r--;
}
while(l<q[i].l){
cnt[a[l-1]]--;
res-=cnt[a[l-1]^k];
l++;
}
ans[q[i].id] = res;
}
forn(i,m) cout <<ans[i]<<'\n';
return 0;
}
- 2019CCPC湘潭站 C题 HDU-6534
莫队+树状数组(这题卡常–线段树会T)
https://cn.vjudge.net/problem/HDU-6534
很容易看出来是莫队题。
题目求l,r内相减绝对值小于K,那么我们可以离散化每个点 v,v+k,v-k然后用树状数组维护出现的次数
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int maxn = 27005;
int n,m,k,z;
struct A{
int l,r,v;
}a[maxn];
vector<int>b;
struct q{
int l,r,id;
}Q[maxn];
ll ans[maxn];
bool cmp(q a,q b){
if(a.l/z==b.l/z) return a.r<b.r;
return a.l<b.l;
}
struct bit{
int node[maxn<<2];
void init(){forn(i,maxn<<2)node[i] = 0;};
inline int lb(int x){return x&(-x);};
void update(int pos,int v){for(int i=pos;i<=b.size();i+=lb(i))node[i]+=v;};
int ask(int pos){int sum = 0;for(int i= pos;i;i-=lb(i))sum+=node[i];return sum;};
inline int query(int l,int r){return ask(r)-ask(l-1);};
}BIT;
void Hash(){
sort(b.begin(),b.end());
b.erase(unique(b.begin(),b.end()),b.end());
for1(i,n){
a[i].l = lower_bound(b.begin(),b.end(),a[i].l)-b.begin()+1;
a[i].v = lower_bound(b.begin(),b.end(),a[i].v)-b.begin()+1;
a[i].r = lower_bound(b.begin(),b.end(),a[i].r)-b.begin()+1;
}
}
int main(){
IO;
cin>>n>>m>>k;
BIT.init();
for1(i,n){
cin>>a[i].v;
a[i].l = a[i].v-k,a[i].r = a[i].v+k;
b.push_back(a[i].v),b.push_back(a[i].l),b.push_back(a[i].r);
}
z = sqrt(n);
forn(i,m){
cin>>Q[i].l>>Q[i].r;
Q[i].id = i;
}
sort(Q,Q+m,cmp);
Hash();
ll res = 0;
int l = 1,r = 0;
forn(i,m){
while(r<Q[i].r) r++,res+=BIT.query(a[r].l,a[r].r),BIT.update(a[r].v,1);
while(r>Q[i].r) BIT.update(a[r].v,-1),res-=BIT.query(a[r].l,a[r].r),r--;
while(l<Q[i].l) BIT.update(a[l].v,-1),res-=BIT.query(a[l].l,a[l].r),l++;
while(l>Q[i].l) l--,res+=BIT.query(a[l].l,a[l].r),BIT.update(a[l].v,1);
ans[Q[i].id] = res;
}
forn(i,m) cout<<ans[i]<<'\n';
return 0;
}
回滚莫队bzoj4241
回滚的话好像只有一篇题解讲得比较好,大致意思是指,莫队排完序之后,在同一块内,右指针是排好序的,当遇到一种情况,我们只能添加不能删除比如维护最大值,就得利用这个性质,每次右指针移动,记录状态,然后左指针移动记录答案然后再回到上次的状态即可,这个复杂度还是n根号n的,如果在同一块中就暴力根号n即可
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i = 0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0)
const int maxn = 1e5+5;
vector<int>b;
int a[maxn],num[maxn],num2[maxn],pos[maxn],has[maxn],n,m;
ll ans[maxn];
struct Q{
int l,r,id;
}q[maxn];
bool cmp(Q a,Q b){
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l]<pos[b.l];
}
void ha(){
sort(b.begin(),b.end());
b.erase(unique(b.begin(),b.end()),b.end());
for1(i,n){
int p = lower_bound(b.begin(),b.end(),a[i])-b.begin();
has[i] = p;
}
}
int main(){
IO;
cin>>n>>m;
int z = sqrt(n);
for1(i,n) {
cin>>a[i];
b.push_back(a[i]);
pos[i] = (i-1)/z;
}
forn(i,m){
cin>>q[i].l>>q[i].r;
q[i].id = i;
}
ha();
sort(q,q+m,cmp);
ll res = 0,res2 = 0;
bool ok = 1;
int l = (pos[q[0].l]+1)*z,r =l-1,l2 = l;
forn(i,m){
if(pos[q[i].l]!=pos[q[i-1].l]){
res = 0,res2 = 0;
l2 = l = (pos[q[i].l]+1)*z,r = l-1;
if(!ok) {
memset(num2,0,sizeof(num2));
ok = 1;
}
}if(pos[q[i].l]==pos[q[i].r]){
res = 0;
for(int j = q[i].l;j<=q[i].r;j++){
num[has[j]]++;
res = max(res,1ll*a[j]*num[has[j]]);
}
for(int j = q[i].l;j<=q[i].r;j++){
num[has[j]]--;
}
}else{
ok = 0;
while(r<q[i].r) r++,num2[has[r]]++,res2 = max(res2,1ll*a[r]*num2[has[r]]);
l = l2,res = res2;
while(l>q[i].l) l--,num2[has[l]]++,res = max(res,1ll*a[l]*num2[has[l]]);
while(l<l2) num2[has[l]]--,l++;
}
ans[q[i].id] = res;
}
forn(i,m) cout<<ans[i]<<'\n';
return 0;
}
带修改莫队,貌似分块n^2/3更快。
bzoj2120
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i = 0;i<n;i++)
#define for1(i,n) for(int i =1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0)
const int maxn = 1e5+5;
const int maxm = 1e6+5;
int a[maxn],num[maxm],pos[maxn],vis[maxn],ans[maxn],pre[maxn],res;
struct Q{
int l,r,id,tim;
}q[maxn];
struct C{
int p,v,pre;
}c[1005];
bool cmp(Q a,Q b){
if(pos[a.l]==pos[b.l]){
if(a.r==b.r) return a.tim<b.tim;
return a.r<b.r;
}
return pos[a.l]<pos[b.l];
}
void cal(int p){
if(vis[p]){
num[a[p]]--;
if(!num[a[p]]) res--;
}else{
if(!num[a[p]]) res++;
num[a[p]]++;
}
vis[p]^=1;
}
void change(int p,int v){
if(vis[p]){
cal(p);
a[p] = v;
cal(p);
}else a[p] = v;
}
int main(){
IO;
int n,m;cin>>n>>m;
int z = sqrt(n);
for1(i,n){
cin>>a[i];
pre[i] = a[i];
pos[i] = i/z;
}
int cntq = 0,cntc = 0;
forn(i,m){
char op;int x,y;cin>>op>>x>>y;
if(op=='Q'){
q[cntq] = {x,y,cntq,cntc};
cntq++;
}else {
c[++cntc] = {x,y,pre[x]};
pre[x] = y;
}
}
sort(q,q+cntq,cmp);
int l = 1,r = 0;
for(int j = 1;j<=q[0].tim;j++) change(c[j].p,c[j].v);
forn(i,cntq){
if(i)for(int j = q[i-1].tim+1;j<=q[i].tim;j++) change(c[j].p,c[j].v);
if(i)for(int j = q[i-1].tim;j>q[i].tim;j--) change(c[j].p,c[j].pre);
while(r<q[i].r) cal(++r);
while(l>q[i].l) cal(--l);
while(r>q[i].r) cal(r--);
while(l<q[i].l) cal(l++);
ans[q[i].id] = res;
}
forn(i,cntq) cout<<ans[i]<<'\n';
return 0;
}