CF2041J Bottle Arrangement
首先考虑暴力 dp,我们按照
也就是设
时间复杂度
于是考虑优化。
发现我们从大往小扫
也就是说我们可以把序列
那么如果一个区间
所以我们只需要去考虑那些
现在的思路就是去维护每个
设现在扫到了第
因为
反之如果
而对于
那么每一次我们用一个 set 维护当前合法的连续段的左端点和长度就可以了,对于那些实际长度
然后每次处理完删去不合法的连续段,其实就是把长度
每次扫从
而这些位置变成
这个东西可以用并查集维护。
这样就做完了,实现中注意第一个位置有一些细节,时间复杂度
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define fi first
#define se second
const int N=1e6+5,inf=1e9;
int n,a[N],b[N],fa[N],ans[N],sz[N],len[N],id[N];
bool vis[N];
set<pii> S;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void ckmn(int &a,int b){a=min(a,b);}
void merge(int u,int v){
u=find(u),v=find(v);
if(u==v) return;
if(ans[u]!=inf) S.erase({sz[u]+len[u],u});
if(ans[v]!=inf) S.erase({sz[v]+len[v],v});
fa[v]=u,sz[u]+=sz[v],len[u]=len[v]=0;
ans[u]=min(ans[u],ans[v]);
if(ans[u]!=inf) S.insert({sz[u],u});
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],ans[i]=inf,sz[i]=1,fa[i]=id[i]=i;
for(int i=1;i<=n;i++) cin>>b[i];
if(n==1){
if(a[1]>b[1]) cout<<"0\n";
else if(a[1]==b[1]) cout<<"1\n";
else cout<<"-1\n";
return 0;
}
sort(id+1,id+n+1,[&](int x,int y){return a[x]>a[y];});
sort(b+1,b+n+1,[&](int x,int y){return x>y;});
for(int i=1,j=1;i<=n;i++){
while(j<=n&&a[id[j]]>b[i]){
int pos=id[j];
if(i==1) ans[pos]=0,S.insert({1,pos});
else if(i==2&&a[pos]==b[1]) ans[pos]=1,S.insert({1,pos});
if(vis[pos-1]) merge(pos-1,pos);
if(vis[pos+1]) merge(pos,pos+1);
vis[pos]=1;
++j;
}
int k=j;
while(k<=n&&a[id[k]]==b[i]){
int pos=id[k];
if(vis[pos-1]){
int l=find(pos-1);
if(ans[l]!=inf&&sz[l]==i-1&&!len[l]){
S.erase({sz[l],l});
S.insert({sz[l]+1,l}),++len[l],++ans[l];
}
}
if(vis[pos+1]){
int l=pos+1;
if(ans[l]!=inf&&sz[l]==i-1&&!len[l]){
S.erase({sz[l],l});
S.insert({sz[l]+1,l}),++len[l],++ans[l];
}
}
++k;
}
while(S.size()&&(*S.begin()).fi<i) ans[(*S.begin()).se]=inf,S.erase(S.begin());
}
if(!S.size()) cout<<"-1\n";
else cout<<ans[(*S.begin()).se]<<'\n';
return 0;
}