【[SDOI2011]攔截導彈】
阿新 • • 發佈:2019-01-01
暴力 轉移 mes span 子序列 isp 序列 cmp mat
這道題是真的蛇皮
方案數要開\(double\)真的蛇皮
首先\(dp\)是非常容易看出來的
設\(dp[i]\)表示以\(i\)結尾的最長子序列
顯然轉移方程為
\[dp[i]=max(dp[j]+1)(j<i,h[j]>=h[i],v[j]>=v[i])\]
暴力轉移是\(O(n^2)\)的
同時第二問我們還需要求一個概率
非常簡單,我們反正做一遍\(dp\),看看\(i\)之後能連接的最長子序列為多少
同時統計好兩邊的方案數,之後如果左右兩邊的長度拼起來等於最長的長度,那麽就可以存在在答案裏,於是概率就是\(\frac{\text{左邊的方案數}\times\text{右邊的方案數}}{\text{總方案數}}\)
之後核心就是求出方案數和子序列長度了
發現這就是一個三維偏序的問題,我們可以直接硬上\(CDQ\)分治
但是像板子裏寫的那樣的\(CDQ\)是不行的,板子裏的\(CDQ\)本質上後根遍歷,所以用左邊更新右邊的時候\(dp\)數組並不能被更新全
於是略改一下板子,改成中根遍歷,先處理左邊,之後用左邊的來更新右邊的\(dp\)值,之後處理右邊
這樣就能夠保證每一個位置被更新的時候其前面的位置都已經被更新了
代碼
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define re register #define maxn 50005 #define lowbit(x) ((x)&(-x)) #define LL double #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) inline int read() { char c=getchar(); int x=0; while(c<‘0‘||c>‘9‘) c=getchar(); while(c>=‘0‘&&c<=‘9‘) x=(x<<3)+(x<<1)+c-48,c=getchar(); return x; } int H[maxn],HH[maxn]; int V[maxn],to[maxn]; int n,tot; int lf[maxn],rf[maxn]; LL ld[maxn],rd[maxn]; inline int find(int x) { int l=1,r=tot; while(l<=r) { int mid=l+r>>1; if(H[mid]==x) return mid; if(H[mid]>x) r=mid-1; else l=mid+1; } return 0; } struct Node { int v,h,ans,rk; LL d; }a[maxn]; inline int cmp(Node A,Node B) { if(A.v==B.v) return A.h<B.h; return A.v<B.v; } inline int cop(Node A,Node B) { return A.rk<B.rk; } int c[maxn]; LL bit[maxn]; inline void add(int x,int y,LL val) { for(re int i=x;i<=tot;i+=lowbit(i)) { if(c[i]>y) continue; if(c[i]==y) bit[i]+=val; if(c[i]<y) c[i]=y,bit[i]=val; } } inline int ask(int x) {int now=-1;for(re int i=x;i;i-=lowbit(i)) if(c[i]) now=max(now,c[i]);return now;} inline LL query(int x,int M) {LL now=0;for(re int i=x;i;i-=lowbit(i)) if(c[i]&&c[i]==M) now+=bit[i];return now;} inline void clear(int x) {for(re int i=x;i<=tot;i+=lowbit(i)) c[i]=0,bit[i]=0;} void CDQ(int s,int t) { if(s==t) return; int mid=s+t>>1; CDQ(s,mid); std::sort(a+s,a+mid+1,cmp),std::sort(a+mid+1,a+t+1,cmp); int i=s,j=mid+1; while(i<=mid&&j<=t) { if(a[i].v<=a[j].v) add(a[i].h,a[i].ans,a[i].d),i++; else { int now=ask(a[j].h); if(now!=-1) { if(now+1>a[j].ans) a[j].ans=now+1,a[j].d=query(a[j].h,now); else if(now+1==a[j].ans) a[j].d+=query(a[j].h,now); } j++; } } while(j<=t) { int now=ask(a[j].h); if(now!=-1) { if(now+1>a[j].ans) a[j].ans=now+1,a[j].d=query(a[j].h,now); else if(now+1==a[j].ans) a[j].d+=query(a[j].h,now); } j++; } for(re int k=s;k<i;k++) clear(a[k].h); std::sort(a+mid+1,a+t+1,cop); CDQ(mid+1,t); } int main() { n=read(); for(re int i=1;i<=n;i++) V[i]=-1*read(),H[i]=-1*read(),HH[i]=H[i],a[i].rk=i; std::sort(H+1,H+n+1); tot=std::unique(H+1,H+n+1)-H-1; for(re int i=1;i<=n;i++) to[i]=find(HH[i]); for(re int i=1;i<=n;i++) a[i].v=V[i],a[i].h=to[i],a[i].ans=1,a[i].d=1; CDQ(1,n); int ans=0;LL now=0; for(re int i=1;i<=n;i++) ans=max(ans,a[i].ans); for(re int i=1;i<=n;i++) if(ans==a[i].ans) now+=a[i].d; printf("%d\n",ans); for(re int i=1;i<=n;i++) lf[a[i].rk]=a[i].ans,ld[a[i].rk]=a[i].d; for(re int i=1;i<=n;i++) V[i]=-1*V[i],HH[i]=-1*HH[i],H[i]=HH[i]; std::sort(H+1,H+n+1); tot=std::unique(H+1,H+n+1)-H-1; for(re int i=1;i<=n;i++) to[i]=find(HH[i]); for(re int i=n;i;--i) a[n-i+1].v=V[i],a[n-i+1].h=to[i],a[n-i+1].rk=n-i+1,a[n-i+1].ans=1,a[n-i+1].d=1; CDQ(1,n); for(re int i=1;i<=n;i++) a[i].rk=n-a[i].rk+1; for(re int i=1;i<=n;i++) rf[a[i].rk]=a[i].ans,rd[a[i].rk]=a[i].d; for(re int i=1;i<=n;i++) { if(rf[i]+lf[i]-1==ans) printf("%.5lf ",rd[i]*ld[i]/now); else printf("0.00000 "); } return 0; }
【[SDOI2011]攔截導彈】