P1972 [SDOI2009]HH的项链 (区间不同颜色个数)
题意:给定一个序列,询问区间内不同颜色的个数
思路:
- 可以用莫队做,不过莫队复杂度是 O ( n n ) O(n\sqrt n) O(nn) 的,1e6的数据显然过不去
- 这里可以离线用 bit 做,以询问的右区间为界,每次出现一个重复的数的时候,就取消掉前面的,尽量更新到靠右的位置。
- 如果是在线的话,可以用到主席树。
BIT代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+10;
int n,m;
int a[maxn],last[maxn],ans[maxn];
struct Opt
{
int l,r,id;
bool operator<(const Opt& b) const
{
return r<b.r;
}
} opt[maxn];
int c[maxn];
int lowbit(int x)
{
return x&-x;
}
void add(int x,int v)
{
for(int i=x; i<=n; i+=lowbit(i))
c[i]+=v;
}
int getsum(int x)
{
int res=0;
for(int i=x; i>0; i-=lowbit(i))
res+=c[i];
return res;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; ++i) scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1; i<=m; ++i) scanf("%d%d",&opt[i].l,&opt[i].r),opt[i].id=i;
sort(opt+1,opt+1+m);
int p=1;
for(int i=1; i<=m; ++i)
{
while(p<=n&&p<=opt[i].r)
{
if(last[a[p]]) add(last[a[p]],-1);
add(p,1);
last[a[p]]=p;
p++;
}
ans[opt[i].id]=getsum(opt[i].r)-getsum(opt[i].l-1);
}
for(int i=1; i<=m; ++i)
printf("%d\n",ans[i]);
return 0;
}
主席树 TLE 的代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+10;
int n,m;
int a[maxn],last[maxn];
int root[maxn],ls[maxn*40],rs[maxn*40],st[maxn*40],no;
int update(int pre,int p,int L,int R,int v)
{
int rt=++no;
st[rt]=st[pre]+v;
ls[rt]=ls[pre];
rs[rt]=rs[pre];
if(L==R) return rt;
int mid=(L+R)>>1;
if(p<=mid) ls[rt]=update(ls[pre],p,L,mid,v);
if(p>mid) rs[rt]=update(rs[pre],p,mid+1,R,v);
return rt;
}
int query(int rt,int l,int r,int L,int R)
{
if(!rt) return 0;
if(l<=L&&R<=r) return st[rt];
int mid=(L+R)>>1;
int ans=0;
if(l<=mid) ans+=query(ls[rt],l,r,L,mid);
if(r>mid) ans+=query(rs[rt],l,r,mid+1,R);
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; ++i)
{
scanf("%d",&a[i]);
if(last[a[i]])
{
root[i]=update(root[i-1],last[a[i]],1,n,-1);
root[i]=update(root[i],i,1,n,1);
}
else root[i]=update(root[i-1],i,1,n,1);
last[a[i]]=i;
}
scanf("%d",&m);
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
int ans=query(root[r],l,r,1,n);
printf("%d\n",ans);
}
return 0;
}
莫队TLE的代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+10;
int n,m,a[maxn],ans[maxn];
int block[maxn],B;
struct Opt
{
int l,r,aid;
bool operator<(const Opt & b) const
{
if(block[l]!=block[b.l]) return l<b.l;
return r<b.r;
}
}opt[maxn];
int nowans,cnt[maxn];
void update(int id,int f)
{
int p=a[id];
cnt[p]+=f;
if(f==1&&cnt[p]==1) nowans++;
if(f==-1&&cnt[p]==0) nowans--;
}
int main()
{
scanf("%d",&n);
B=sqrt(n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
block[i]=i/B;
}
scanf("%d",&m);
for(int i=1;i<=m;++i)
scanf("%d%d",&opt[i].l,&opt[i].r),opt[i].aid=i;
sort(opt+1,opt+1+m);
int l=1,r=0;
for(int i=1;i<=m;++i)
{
while(l<opt[i].l) update(l++,-1);
while(l>opt[i].l) update(--l,+1);
while(r<opt[i].r) update(++r,+1);
while(r>opt[i].r) update(r--,-1);
ans[opt[i].aid]=nowans;
}
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
return 0;
}
H. Interval 2020暑期牛客第五场
题意:求区间
[
l
,
r
]
[l,r]
[l,r] 内所有子区间连续 & 的不同值的个数
思路:一个数最多 30 位连续取 &最多变化30次,我们把所有的
[
l
,
r
]
[l,r]
[l,r] 的值都算出来,处理出三元组
(
l
,
r
,
k
)
(l,r,k)
(l,r,k)。最多有 30n 个三元组。然后就类似统计区间不同的颜色的个数,这里强制在线,那么就更新到主席树上。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
int n,q;
int a[maxn],last[30];
int root[maxn*30],ls[maxn*30*40],rs[maxn*30*40],st[maxn*30*40],no;
vector<int> vec;
vector<pair<int,int> > opt[maxn];
unordered_map<int,int> pre;
int update(int pre,int p,int L,int R,int v)
{
int rt=++no;
st[rt]=st[pre]+v;
ls[rt]=ls[pre];
rs[rt]=rs[pre];
if(L==R) return rt;
int mid=(L+R)>>1;
if(p<=mid) ls[rt]=update(ls[pre],p,L,mid,v);
if(p>mid) rs[rt]=update(rs[pre],p,mid+1,R,v);
return rt;
}
int query(int rt,int l,int r,int L,int R)
{
if(!rt) return 0;
if(l<=L&&R<=r) return st[rt];
int mid=(L+R)>>1;
int ans=0;
if(l<=mid) ans+=query(ls[rt],l,r,L,mid);
if(r>mid) ans+=query(rs[rt],l,r,mid+1,R);
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i)
{
vec.clear();
for(int j=0;j<30;++j)
if(a[i]>>j&1) vec.push_back(last[j]);
else last[j]=i;
sort(vec.begin(),vec.end(),greater<int>());
vec.resize(unique(vec.begin(),vec.end())-vec.begin());
opt[i].push_back({i,a[i]});
int cur=a[i];
for(auto j: vec)
{
if(!j) break;
cur&=a[j];
opt[i].push_back({j,cur});
}
}
for(int i=1;i<=n;++i)
{
int rt=root[i-1];
for(auto x: opt[i])
{
int l=x.first,v=x.second;
if(!pre[v]) root[i]=update(rt,l,1,n,1);
else if(pre[v]<l)
{
root[i]=update(rt,pre[v],1,n,-1);
root[i]=update(root[i],l,1,n,1);
}
pre[v]=l;
rt=root[i];
}
}
scanf("%d",&q);
int ans=0;
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
l=(l^ans)%n+1,r=(r^ans)%n+1;
if(l>r) swap(l,r);
ans=query(root[r],l,r,1,n);
printf("%d\n",ans);
}
return 0;
}
这里与上面的区间是把一个值都更新到每一个包含它的区间上。比如, l = 2 , r = 3 , k = 2 l=2,r=3,k=2 l=2,r=3,k=2。那么这个 k k k 包含在 [ 1 , 3 ] 、 [ 2 , 3 ] [1,3]、[2,3] [1,3]、[2,3] 两个区间上。所以做区间更新 [ 1 , 2 ] [1,2] [1,2] ,然后查询的时候,查询 [ l , r ] [l,r] [l,r] 只需要在 r o o t [ r ] root[r] root[r] 这棵树上单点查询 l l l 这个点即可。更新的时候需要注意不能重复更新。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
int n,q;
int a[maxn],last[30];
int root[maxn*30],ls[maxn*30*40],rs[maxn*30*40],st[maxn*30*40],no;
vector<int> vec;
unordered_map<int,int> maxl;
int update(int pre,int l,int r,int L,int R)
{
int rt=++no;
ls[rt]=ls[pre];
rs[rt]=rs[pre];
st[rt]=st[pre];
if(l<=L&&R<=r)
{
st[rt]++;
return rt;
}
int mid=(L+R)>>1;
if(l<=mid) ls[rt]=update(ls[pre],l,r,L,mid);
if(r>mid) rs[rt]=update(rs[pre],l,r,mid+1,R);
return rt;
}
int query(int rt,int p,int L,int R)
{
if(!rt) return 0;
if(L==R) return st[rt];
int mid=(L+R)>>1;
if(p<=mid) return st[rt]+query(ls[rt],p,L,mid);
if(p>mid) return st[rt]+query(rs[rt],p,mid+1,R);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i)
{
vec.clear();
for(int j=0;j<30;++j)
if(a[i]>>j&1) vec.push_back(last[j]);
else last[j]=i;
sort(vec.begin(),vec.end(),greater<int>());
vec.resize(unique(vec.begin(),vec.end())-vec.begin());
int cur=a[i];
int l=maxl[cur]+1,r=i;
if(l<=r)
{
root[i]=update(root[i-1],l,r,1,n);
maxl[cur]=i;
}
for(auto j: vec)
{
if(!j) break;
cur&=a[j];
l=maxl[cur]+1,r=j;
if(l<=r)
{
root[i]=update(root[i],l,r,1,n);
maxl[cur]=j;
}
}
}
scanf("%d",&q);
int ans=0;
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
l=(l^ans)%n+1,r=(r^ans)%n+1;
if(l>r) swap(l,r);
ans=query(root[r],l,1,n);
printf("%d\n",ans);
}
return 0;
}