先来看个弱化版
思路
如果直接维护区间内的不同颜色,将会很难维护。
记
l
a
s
t
i
last_i
lasti 为
a
i
a_i
ai 上一次出现的位置,如果前面没出现过则等于 0。
考虑哪些点才会对答案有贡献:只有在询问区间内第一次出现的点才会有贡献。也就是说,假如我们询问的是
[
l
,
r
]
[l,r]
[l,r],只有
l
a
s
t
i
<
l
,
(
l
≤
i
≤
r
)
last_i < l, (l\leq i\leq r)
lasti<l,(l≤i≤r) 的位置才能产生贡献。
不难发现,这是一个经典的二维偏序问题。离线下来随便维护一下就行。
但是如果题目要求强制在线,我们就可以用主席树来解决。用权值线段树维护 l a s t i last_i lasti,用每一个根代表 i i i,查询只需要查询根的范围在 r o o t l root_l rootl ~ r o o t r root_r rootr, l a s t i ≤ l − 1 last_i \leq l-1 lasti≤l−1 的答案即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct Seg
{
int ls,rs,val;
Seg()
{
ls=rs=val=0;
}
};
vector<Seg> tr;
vector<int> root;
int insert(int u,int st,int ed,int x)
{
tr.push_back(tr[u]);
u=tr.size()-1;
tr[u].val++;
if(st==ed) return u;
int mid=st+ed>>1;
if(x<=mid)
tr[u].ls=insert(tr[u].ls,st,mid,x);
else
tr[u].rs=insert(tr[u].rs,mid+1,ed,x);
return u;
}
int query(int u,int v,int st,int ed,int l,int r)
{
if(l<=st&&ed<=r)
{
// cout<<st<<" "<<ed<<" "<<tr[v].val<<" "<<tr[u].val<<"\n";
return tr[v].val-tr[u].val;
}
int mid=st+ed>>1;
int res=0;
if(mid>=l)
res+=query(tr[u].ls,tr[v].ls,st,mid,l,r);
if(mid<r)
res+=query(tr[u].rs,tr[v].rs,mid+1,ed,l,r);
return res;
}
void O_o()
{
int n;
cin>>n;
vector<int> a(n+1),ls(n+1);
map<int,int> mp;
for(int i=1; i<=n; i++)
{
cin>>a[i];
ls[i]=mp[a[i]];
mp[a[i]]=i;
}
root.clear(); tr.clear();
root.push_back(0);
tr.push_back(Seg());
for(int i=1; i<=n; i++)
{
// int t=;
root.push_back(root.back());
root[i]=insert(root[i-1],0,n,ls[i]);
}
int q;
cin>>q;
while(q--)
{
int l,r;
cin>>l>>r;
cout<<query(root[l-1],root[r],0,n,0,l-1)<<"\n";
}
}
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cout<<fixed<<setprecision(12);
int T=1;
// cin>>T;
while(T--)
{
O_o();
}
}
CF786C
思路
很显然,线段的个数是调和级数
O
(
n
log
n
)
O(n\log n)
O(nlogn)
当左端点固定时,右端点可以用二分来确定
但是主席树+二分是
O
(
log
2
n
)
O(\log^2n)
O(log2n) 的,无法接受。
怎么样把它变成主席树上二分呢?其实用线段树维护
i
i
i,每一棵线段树对应一个
l
a
s
t
i
last_i
lasti 就好啦
时间复杂度
O
(
n
log
2
n
)
O(n\log^2n)
O(nlog2n)
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct Seg
{
int ls,rs,val;
Seg()
{
ls=rs=val=0;
}
};
vector<Seg> tr;
vector<int> root,id;
int insert(int u,int st,int ed,int x)
{
tr.push_back(tr[u]);
u=tr.size()-1;
tr[u].val++;
if(st==ed) return u;
int mid=st+ed>>1;
if(mid>=x)
tr[u].ls=insert(tr[u].ls,st,mid,x);
else
tr[u].rs=insert(tr[u].rs,mid+1,ed,x);
return u;
}
//int query(int u,int v,int st,int ed,int l,int r)
//{
// if(l<=st&&ed<=r)
// {
// return tr[v].val-tr[u].val;
// }
// int mid=st+ed>>1;
// int res=0;
// if(mid>=l)
// res+=query(tr[u].ls,tr[v].ls,st,mid,l,r);
// if(mid<r)
// res+=query(tr[u].rs,tr[v].rs,mid+1,ed,l,r);
// return res;
//}
array<int,2> find(int u,int v,int st,int ed,int l,int r,int k)//pos,val
{
if(ed<l||st>r||k<=0) return {0,0};
if(st==ed)
{
return {st,tr[v].val-tr[u].val};
}
int mid=st+ed>>1;
if(l<=st&&ed<=r)
{
int res=tr[tr[v].ls].val-tr[tr[u].ls].val;
if(res>=k)
return {find(tr[u].ls,tr[v].ls,st,mid,l,r,k)[0],tr[v].val-tr[u].val};
else
return {find(tr[u].rs,tr[v].rs,mid+1,ed,l,r,k-res)[0],tr[v].val-tr[u].val};
}
auto lres=find(tr[u].ls,tr[v].ls,st,mid,l,r,k);
auto rres=find(tr[u].rs,tr[v].rs,mid+1,ed,l,r,k-lres[1]);
return {max(lres[0],rres[0]),lres[1]+rres[1]};
}
void O_o()
{
int n;
cin>>n;
vector<int> a(n+1),ls(n+1);
map<int,int> mp;
vector<vector<int>> p(n+1);
for(int i=1; i<=n; i++)
{
cin>>a[i];
ls[i]=mp[a[i]];
mp[a[i]]=i;
p[ls[i]].push_back(i);
}
tr.assign(1,Seg());
root.assign(1,0);
id.assign(n+1,0);
for(int i=0; i<=n; i++)
{
for(auto x:p[i])
{
root.push_back(root.back());
root[root.size()-1]=insert(root[root.size()-1],1,n+1,x);
}
id[i]=root[root.size()-1];
}
for(int k=1; k<=n; k++)
{
int now=1,ans=0;
while(now<=n)
{
now=find(0,id[now-1],1,n+1,now,n+1,k+1)[0];
ans++;
}
cout<<ans<<" ";
}
}
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cout<<fixed<<setprecision(12);
int T=1;
// cin>>T;
while(T--)
{
O_o();
}
}