题解:CF1969E Unique Array
jiazhichen844 · · 题解
题意:定义一个数组
首先,一个显然的策略,设把
所以,题意可以理解为,至少分成多少段,使得每段均合法(段与段之间有
此时,有一个显然的贪心,从头扫到尾,若扫到一个前缀不合法,就把这个前缀断开,去掉结尾,分成一段。
证明也是显然的,首先,不可能延后断开位置,这样会使其不合法,若提前了断开的位置,相当于给后面的前缀前面插入若干数,这样不会新前缀的断开位置变优。
然后,我们考虑如何判断一段前缀是否合法。
注意到,若扫到了
数据结构就用一个区间加,求区间
代码:
#include<bits/stdc++.h>
using namespace std;
struct segment_tree
{
int l,r,lazy,minn;
}a[1200005];
int b[300005];
const int inf=0x3f3f3f3f;
void build(int pos,int l,int r)
{
a[pos]={l,r,0,0};
if(l==r)
return;
int mid=l+r>>1;
build(2*pos,l,mid);
build(2*pos+1,mid+1,r);
}
void update(int pos)
{
a[pos].minn=min(a[2*pos].minn,a[2*pos+1].minn);
}
void putlazy(int pos,int x)
{
a[pos].lazy+=x;
a[pos].minn+=x;
}
void pushdown(int pos)
{
putlazy(2*pos,a[pos].lazy);
putlazy(2*pos+1,a[pos].lazy);
a[pos].lazy=0;
}
void add(int pos,int l,int r,int x)
{
if(r<a[pos].l||l>a[pos].r||l>r)
return;
if(l<=a[pos].l&&r>=a[pos].r)
{
putlazy(pos,x);
return;
}
pushdown(pos);
add(2*pos,l,r,x);
add(2*pos+1,l,r,x);
update(pos);
}
int getmin(int pos,int l,int r)
{
if(r<a[pos].l||l>a[pos].r)
return inf;
if(l<=a[pos].l&&r>=a[pos].r)
return a[pos].minn;
pushdown(pos);
return min(getmin(2*pos,l,r),getmin(2*pos+1,l,r));
}
int t1[300005],t2[300005];
void test()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>b[i];
for(int i=1;i<=n;i++)
t1[i]=t2[i]=0;
build(1,1,n);
int pre=1,cnt=0;
for(int i=1;i<=n;i++)
{
add(1,t2[b[i]]+1,t1[b[i]],-1);
add(1,t1[b[i]]+1,i,1);
t2[b[i]]=t1[b[i]];
t1[b[i]]=i;
if(getmin(1,pre,i)==0)
{
// cout<<i<<" ";
cnt++;
pre=i+1;
}
}
cout<<cnt<<"\n";
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
for(int i=1;i<=t;i++)
test();
}