CF1905F Field Should Not Be Empty

考虑直接计算贡献,对于每个点找出那些交换会使其产生贡献,排除本来就是好的排列之后,发现交换一定更优,接下来分类讨论。

  1. \(a_i=i\)
    • 这个时候如果左边数都小于他,那么这个数是合法的,直接计入答案,因为我们肯定不会交换这个数两侧的,因为左侧数小于右侧一定不优,直接计入答案即可。
    • 否则这个数如果左右只有一对数不合法,交换这一对即可。
  2. \(a_i>i\)
  • 考虑能不能交换 \(a_i\)\(a_{a_i}\) 来让 \(a_i\) 有贡献,充分必要条件就是有 \(a_i-1\) 个小于 \(a_i\) 的数在 \([1,a_i]\) 之间。
  1. \(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;
}
posted @ 2025-11-20 19:01  NeeDna  阅读(9)  评论(0)    收藏  举报