#樹狀陣列,dp#SGU 521 North-East
阿新 • • 發佈:2022-03-25
題目
在平面上有 \(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; }