1. 程式人生 > 其它 >#樹狀陣列,dp#SGU 521 North-East

#樹狀陣列,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;
}