CF650D Zip-line

一开始连题解都看不懂,对着题解敲了一遍算是会了(

题意:给定一个序列,对于每次询问,输出把这位数改成另一个数后的LIS长度。

下面的方法是通过 离线+树状数组 的解法做的。

核心的思想是在每修改一位时,这一位前面和后面的序列是不变的,并且LIS可以拆分为以该位开头的LIS与以该位结尾的LIS,所以这一位前面的LIS长度以及这一位后面的LIS长度都可以利用起来。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
int read()
{
	unsigned n=0; int f=1; char ch;
	for(ch=getchar();ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
	for(;'0'<=ch&&ch<='9';ch=getchar()) n=(n<<3)+(n<<1)+(ch^48);
	return f*n;
}
//#define DEBUG 1
const int N=5E5+5;
int n,m;
int h[N],b[N<<1],cnt,now;
int f[N],g[N],len,lis[N];
//f[i]-以i为终点的LIS长度
//g[i]-以i为起点的LIS长度
struct ask
{
	int id,x,val;
	int f,g;
}qu[N];
bool cmp1(ask a,ask b){return a.x==b.x?a.id<b.id:a.x<b.x;}
int res[N];
#define x(i) qu[i].x
#define F(i) qu[i].f
#define G(i) qu[i].g
#define res(i) res[qu[i].id]

int tree[N<<1];//维护1~x的数中dp值的最大值 
inline int lowbit(int x){return x&-x;}
inline void update(int x,int k){for(;x<=cnt;x+=lowbit(x)) tree[x]=max(tree[x],k);} 
inline int query(int x){int ans=0; for(;x;x-=lowbit(x)) ans=max(ans,tree[x]); return ans;}

inline void Solve()
{
	#ifdef DEBUG
	printf("Debuging...\n");
	#endif

	n=read(),m=read();
	for(int i=1;i<=n;i++) b[i]=h[i]=read();
	cnt=n;
	for(int i=1;i<=m;i++)
		qu[i].id=i,qu[i].x=read(),b[++cnt]=qu[i].val=read();
	//离散化 
	sort(b+1,b+cnt+1);
	cnt=unique(b+1,b+cnt+1)-b-1;
	for(int i=1;i<=n;i++) h[i]=lower_bound(b+1,b+cnt+1,h[i])-b;
	for(int i=1;i<=m;i++) qu[i].val=lower_bound(b+1,b+cnt+1,qu[i].val)-b;
	//dp处理f,g
	//f-> 1->i的最长上升子序列 
	memset(tree,0,sizeof(tree));
	for(int i=1;i<=n;i++) f[i]=query(h[i]-1)+1,update(h[i],f[i]);
	//g-> n->i的最长下降子序列 
		//我们将求h[n->i]的最长下降子序列,转化为求cnt-h[n->i]+1的最长上升子序列 
	memset(tree,0,sizeof(tree));
	for(int i=n;i>=1;i--) g[i]=query(cnt-h[i])+1,update(cnt-h[i]+1,g[i]);
	//求LIS的长度len,并找出LIS某一位中的可能的数的数量
	//过i点的LIS长度为 f[i]+g[i]-1 
	for(int i=1;i<=n;i++) len=max(len,f[i]+g[i]-1); 
	for(int i=1;i<=n;i++) if(f[i]+g[i]-1==len) ++lis[f[i]];//这位数可以在LIS的f[i]位,记录
	//离线处理询问
	sort(qu+1,qu+m+1,cmp1);//按x排序 
	//处理出改变第i位时的f[i] 
		//now-当前处理到的位的前一位 
		//now每推进一位就把该位的f[i]加入树状数组 
		//这样查询出的F(i)就是以i-1位结尾的LIS长度 
	memset(tree,0,sizeof(tree)),now=1;
	for(int i=1;i<=m;i++)
	{
		for(; now < qu[i].x ; update(h[now],f[now]),++now);
		F(i)=query(qu[i].val-1);
	}
	//处理出改变第i位时的g[i],与上面同理 
	memset(tree,0,sizeof(tree)),now=n;
	for(int i=m;i>=1;i--)
	{
		for(; now > qu[i].x ; update(cnt-h[now]+1,g[now]),--now);
		G(i)=query(cnt-qu[i].val);
		if(F(i)+G(i)+1>len) res(i)=F(i)+G(i)+1;
	}
	//根据F(i)和G(i)情况分类讨论
	//i点修改后过i点的为 F(i)+G(i)+1
		//如果修改后过i点的LIS长度短于len,并且这个点是LIS该位上的唯一点,那么LIS只能将该位剔除,答案为len-1
		//否则取修前与修后LIS长度的最大值(增长的才能让答案更新) 
	for(int i=1;i<=m;i++)
	if(!res(i))
	{
		if( F(i)+G(i)+1<len && f[x(i)]+g[x(i)]-1==len && lis[f[x(i)]]==1) res(i)=len-1;
		else res(i)=max(F(i)+G(i)+1,len);
	}
	//输出结果 
	for(int i=1;i<=m;i++) printf("%d\n",res[i]);
	
	return ;
}
//#define FILES_IO 2
int main()
{
	#ifdef FILES_IO
	string FileName="test",ReadFile=FileName+".in",WriteFile=FileName+".out";
	freopen(ReadFile.c_str(),"r", stdin);
	freopen(WriteFile.c_str(),"w", stdout);
	#endif
	//srand((unsigned)time(0));
	//ios::sync_with_stdio(false);
	//cin.tie(0);

	int T=1;
	//scanf("%d",&T);
	for(;T--;)
		Solve();

	#ifdef FILES_IO
	fclose(stdin);
	fclose(stdout);
	#endif
	return 0;
}

上一篇:JS 新浪下拉列表页


下一篇:python基础练习题(题目 有序列表插入元素)