#树状数组,dp#SGU 521 North-East

题目

在平面上有 \(n\) 个点,现在有一个人要从某个点出发,

每次只能到达横纵坐标都超过原坐标的点,也就是 \(x_j<x_i,y_j<y_i\)

如果他要经过最多的点,那么哪些点是可能到达的,哪些点是必须到达的。


分析

按横坐标排序,实际上只需要管纵坐标,离散化之后直接用树状数组维护 \(dp[i]=dp[j]+1(y_j<y_i)\)

然后将序列翻转再做一遍,可能到达也就是 \(dp[i]+dp'[i]=ans-1\)

一定到达就是在可能到达的基础上,要保证 \(dp[i]\) 唯一,再统计一下 \(dp[i]\) 的个数即可。


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int N=100011;
struct rec{int x,y,rk;}a[N];
int c[N],n,ans[N],mx,f[N],g[N],m,b[N];
int iut(){
	int ans=0,f=1; char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans*f;
}
bool cmp(rec x,rec y){
	return x.x<y.x||(x.x==y.x&&x.y<y.y);
}
int max(int a,int b){return a>b?a:b;}
void update(int x,int y){
	for (;x<=m;x+=-x&x) c[x]=max(c[x],y);
}
int query(int x){
	int ans=0;
	for (;x;x-=-x&x) ans=max(ans,c[x]);
	return ans;
}
int main(){
	n=iut();
	for (int i=1;i<=n;++i) a[i]=(rec){iut(),b[i]=iut(),i};
	sort(a+1,a+1+n,cmp),sort(b+1,b+1+n),m=unique(b+1,b+1+n)-b-1;
	for (int i=1;i<=n;++i) a[i].y=lower_bound(b+1,b+1+m,a[i].y)-b; 
	for (int l=1,r;l<=n;l=r+1){
		for (r=l;r<=n&&a[r].x==a[l].x;++r); --r;
		for (int i=l;i<=r;++i) f[i]=query(a[i].y-1)+1;
		for (int i=l;i<=r;++i) update(a[i].y,f[i]); 
	}
	for (int i=1;i<=m;++i) c[i]=0;
	for (int r=n,l;r;r=l-1){
		for (l=r;l&&a[l].x==a[r].x;--l); ++l;
		for (int i=l;i<=r;++i) g[i]=query(m-a[i].y)+1;
		for (int i=l;i<=r;++i) update(m-a[i].y+1,g[i]);
    }
    for (int i=1;i<=m;++i) c[i]=0;
	for (int i=1;i<=n;++i) mx=max(mx,f[i]);
	for (int i=1;i<=n;++i)
	    if (f[i]+g[i]-1==mx) ans[++ans[0]]=a[i].rk,++c[f[i]];
	sort(ans+1,ans+1+ans[0]);
	printf("%d ",ans[0]);
	for (int i=1;i<=ans[0];++i) printf("%d ",ans[i]);
	putchar(10),ans[0]=0;
	for (int i=1;i<=n;++i)
	    if (f[i]+g[i]-1==mx&&c[f[i]]==1) ans[++ans[0]]=a[i].rk;
	sort(ans+1,ans+1+ans[0]),printf("%d ",ans[0]);
	for (int i=1;i<=ans[0];++i) printf("%d ",ans[i]);
	return 0;
}
posted @ 2022-03-25 12:06  lemondinosaur  阅读(36)  评论(0编辑  收藏  举报