CF1905F Field Should Not Be Empty
考虑直接计算贡献,对于每个点找出那些交换会使其产生贡献,排除本来就是好的排列之后,发现交换一定更优,接下来分类讨论。
- \(a_i=i\)
- 这个时候如果左边数都小于他,那么这个数是合法的,直接计入答案,因为我们肯定不会交换这个数两侧的,因为左侧数小于右侧一定不优,直接计入答案即可。
- 否则这个数如果左右只有一对数不合法,交换这一对即可。
- \(a_i>i\)
- 考虑能不能交换 \(a_i\) 和 \(a_{a_i}\) 来让 \(a_i\) 有贡献,充分必要条件就是有 \(a_i-1\) 个小于 \(a_i\) 的数在 \([1,a_i]\) 之间。
- \(a_i<i\)
- 考虑能不能交换 \(a_i\) 和 \(a_{a_i}\) 来让 \(a_i\) 有贡献,充分必要条件就是 \(a_i\) 是 \([1,a_i]\) 之间最大的数字。
找到每个交换的最大值就可以了。原理是这种交换的总数是 \(O(n)\) 的。
code:
#include<bits/stdc++.h>
#define fi first
#define se second
#define int long long
#define lbt(x) (x&(-x))
#define pii pair<int,int>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=5e5+10;
int n,m,k,T,a[N],p[N],mx[N],mxp[N],t[N],ans;
map<pii,int> g;
void upd(int i,int x){while(i<N){t[i]+=x;i+=lbt(i);}}
int sc(int i){int ans=0;while(i>0){ans+=t[i];i-=lbt(i);}return ans;}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--){
cin>>n;g.clear();ans=0;
rep(i,1,n){t[i]=0;
cin>>p[i];a[p[i]]=i;mx[i]=max(mx[i-1],p[i]);
}rep(i,1,n) mxp[i]=max(mxp[i-1],a[i]);
rep(i,1,n){
upd(p[i],1);
if(p[i]==i){
if(mx[i]==i) ans++;
if(sc(i)==i-1) g[{a[mx[i]],mxp[i]}]++;
}else if(p[i]>i){
if(mx[p[i]]==p[i]) g[{i,p[i]}]++;
}else if(mxp[p[i]-1]==p[i]-1) g[{p[i],i}]++;
}int ma=-2;
for(auto x:g) ma=max(ma,x.se);
cout<<ans+ma<<'\n';
}
return 0;
}

浙公网安备 33010602011771号