Field Should Not Be Empty

这道题目的思想非常新

首先我们按照比较传统的想法,考虑交换两个位置\(i\)\(j\)能带来什么影响

然后这就是这道题目的精华所在了,我们考虑影响的时候,没有必要去精确每一个位置的两个信息(左边更大的数的个数和右边更小的数的个数)怎么样变化,而是只用考虑这一次交换会让答案增大多少(其实重新做这道题目的时候就是这么想的,所以以后的只修改一次的题目就可以从这两个方向去想)

我们先考虑\((i,j)\),不难发现,如果某一个数(设为\(x\))左边恰好有一个数比其大,右边恰好有一个数比其小,而且恰好分别对应下标\(i\)\(j\),那么\(x\)就会对\(i,j\)的交换产生一个贡献

再考虑\(i\)\(j\)是否会产生贡献,比如\(i\),显然只有将\(i\)交换到下标\(p_i\)时才会产生贡献

而在区间\([i,j]\)外面的都不用考虑

代码见下

#include<bits/stdc++.h>
#define ll long long
#define ULL unsigned long long
using namespace std;
const int N=2e5+10;
int n;
int f[N][2],mx[N][3];
//f[i][0]表示第i个数前面唯一一个大于其的数
//f[i][1]表示第i个数后面唯一一个小于其的数
vector<int> G[N]; 
int c[N];
int segmax[N<<2],lazy[N<<2];
int p[N],pos[N],vis[N];
//pos[i]表示i的下标,vis用来判断数在原序列中是否是good,如果为2则代表为good 
void add(int x)
{
	for(;x<=n;x+=x&-x) c[x]++;
}
int ask(int x)
{
	int res=0;
	for(;x>=1;x-=x&-x) res+=c[x];
	return res;
}
void build(int p,int l,int r)
{
	if(l==r)
	{
		segmax[p]=lazy[p]=0;
		return;
	}
	int mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	segmax[p]=lazy[p]=0; 
}
void pushdown(int p)
{
	segmax[p<<1]+=lazy[p],lazy[p<<1]+=lazy[p];
	segmax[p<<1|1]+=lazy[p],lazy[p<<1|1]+=lazy[p];
	lazy[p]=0;
}
void modify(int p,int l,int r,int x,int d)
{
	if(l>x||r<x) return;
	if(l==r)
	{
		segmax[p]+=d;
		return ;
	}
	if(lazy[p]) pushdown(p);
	int mid=l+r>>1;
	modify(p<<1,l,mid,x,d);
	modify(p<<1|1,mid+1,r,x,d);
	segmax[p]=max(segmax[p<<1],segmax[p<<1|1]);
}
void modify1(int p,int l,int r,int x,int y,int d)
{
	if(l>y||r<x) return;
	if(l>=x&&r<=y)
	{
		segmax[p]+=d;
		lazy[p]+=d;
		return;
	}
	if(lazy[p]) pushdown(p);
	int mid=l+r>>1;
	modify1(p<<1,l,mid,x,y,d);
	modify1(p<<1|1,mid+1,r,x,y,d);
	segmax[p]=max(segmax[p<<1],segmax[p<<1|1]);
}
int getmax(int p,int l,int r,int x,int y)
{
	if(l>y||r<x) return -0x7fffffff;
	if(l>=x&&r<=y) return segmax[p];
	if(lazy[p]) pushdown(p);
	int mid=l+r>>1;
	return max(getmax(p<<1,l,mid,x,y),getmax(p<<1|1,mid+1,r,x,y));
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		for(int j=0;j<=1;j++)
		f[i][j]=0;
		for(int i=1;i<=n;i++)
		G[i].clear(),c[i]=vis[i]=0;
		build(1,1,n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&p[i]);
			pos[p[i]]=i;
		 } 
		mx[1][0]=p[1],mx[n][1]=p[n];
		for(int i=2;i<=n;i++) mx[i][0]=max(mx[i-1][0],p[i]);
		for(int i=n-1;i>=1;i--) mx[i][1]=min(mx[i+1][1],p[i]);
		for(int i=1;i<=n;i++)
		{
			int u=ask(n)-ask(p[i]);//这里一定要用树状数组判断
			//否则边界情况讨论太麻烦了 
			if(u==1) f[i][0]=mx[i-1][0];
			else if(!u) vis[i]++;
			add(p[i]);	
		}
		for(int i=1;i<=n;i++) c[i]=0;
		for(int i=n;i>=1;i--)
		{
			int u=ask(p[i]-1);
			if(u==1) f[i][1]=mx[i+1][1];
			else if(!u) vis[i]++;
			add(p[i]);
		}
		int sum=0,Max=-0x7fffffff;
		for(int i=1;i<=n;i++)
		if(vis[i]==2) sum++;
		else if(f[i][0]&&f[i][1]) G[pos[f[i][1]]].push_back(pos[f[i][0]]);
		for(int i=1;i<=n;i++)
		{
			for(int j=0;j<G[i].size();j++)
			{
				int u=G[i][j];
				modify(1,1,n,u,1);
			} 
			if(p[i]<i&&(i==1||mx[p[i]-1][0]==p[i]-1))
			modify(1,1,n,p[i],1);
			else if(vis[i]==2) modify1(1,1,n,1,i-1,-1);
			if(pos[i]<i&&(i==n||mx[i+1][1]==i+1))
			modify(1,1,n,pos[i],1);
			else if(vis[pos[i]]==2) modify(1,1,n,pos[i],-1);
			if(i>1) Max=max(Max,getmax(1,1,n,1,i-1));
			for(int j=0;j<G[i].size();j++)
			{
				int u=G[i][j];
				modify(1,1,n,u,-1);
			} 
			if(p[i]<i&&(i==1||mx[p[i]-1][0]==p[i]-1))
			modify(1,1,n,p[i],-1);
			else if(vis[i]==2) modify1(1,1,n,1,i-1,1);
			if(pos[i]<i&&(i==n||mx[i+1][1]==i+1))
			modify(1,1,n,pos[i],-1);
			else if(vis[pos[i]]==2) modify(1,1,n,pos[i],1);
			if(vis[i]==2) modify1(1,1,n,1,i,-1);
		}
		printf("%d\n",sum+Max);
	}
	return 0;
}
posted @ 2024-02-25 22:18  最爱丁珰  阅读(5)  评论(0编辑  收藏  举报