bzoj2244[SDOI2011]拦截导弹

题意:最长不上升子序列,但有两个关键字.求:1.最长不上升子序列的长度 2.随机在最长不上升子序列中选取一个,问某个位置被选中的概率.

调到快怀疑人生最后发现把printf(“%.8f”,0)改成printf(“%.8f”,0.0”)就能过了……论学好输入输出的必要性….

记f[0][i]表示以位置i结尾的最长不上升子序列长度,f[1][i]表示从i开始的最长不上升子序列长度.

可以发现第二问只要算出以每个位置开始和结束的最长不上升子序列的方案数g[0][i]和g[1][i],如果f[0][i]+f[1][i]-1等于整个序列的最长不上升子序列长度,那么i出现的概率为g[0][i]*g[1][i]/(所有的最长不上升子序列种数)

f数组只需要正反两遍cdq分治就可以求出.对于g数组,我们回忆一个关键字的最长不上升子序列方案数的求法,可以和f数组一起dp.只要在树状数组每个位置同时维护一段区间的最大值和最大值的数目就可以了.

Cdq分治的时候细节不会处理我就大力sort…不过还是O(nlog^2n)的.

方案数用double存就可以过

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long double ll;
const int maxn=50005;
int Max[maxn];ll cntMax[maxn];
void Add1(int x,int w,ll cnt){
  for(;x;x-=x&(-x)){
    if(Max[x]==w){
      cntMax[x]+=cnt;
    }else if(Max[x]<w){
      Max[x]=w;cntMax[x]=cnt;
    }
  }
}
int qMax1(int x){
  int ans=0;
  for(;x<maxn;x+=x&(-x))if(Max[x]>ans)ans=Max[x];
  return ans;
}
ll qSum1(int x,int M){
  ll ans=0;
  for(;x<maxn;x+=x&(-x))if(Max[x]==M)ans+=cntMax[x];
  return ans;
}
void del1(int x){
  for(;x;x-=x&(-x))Max[x]=cntMax[x]=0;
}
void Add2(int x,int w,ll cnt){
  for(;x<maxn;x+=x&(-x)){
    if(Max[x]==w){
      cntMax[x]+=cnt;
    }else if(Max[x]<w){
      Max[x]=w;cntMax[x]=cnt;
    }
  }
}
int qMax2(int x){
  int ans=0;
  for(;x;x-=x&(-x))if(Max[x]>ans)ans=Max[x];
  return ans;
}
ll qSum2(int x,int M){
  ll ans=0;
  for(;x;x-=x&(-x))if(Max[x]==M)ans+=cntMax[x];
  return ans;
}
void del2(int x){
  for(;x<maxn;x+=x&(-x))Max[x]=cntMax[x]=0;
}
int h[2][maxn],v[2][maxn];
int f[2][maxn];ll g[2][maxn];
int seq[maxn];
bool cmp1(const int &a,const int &b){
  return h[0][a]<h[0][b];
}
bool cmp2(const int &a,const int &b){
  return v[0][a]<v[0][b];
}
void cdq1(int l,int r){
  if(l==r)return;
  int mid=(l+r)>>1;
  cdq1(l,mid);
  for(int i=l;i<=r;++i)seq[i]=i;
  sort(seq+l,seq+mid+1,cmp2);
  sort(seq+mid+1,seq+r+1,cmp2);
  int pt=mid;
  for(int i=r;i>mid;--i){
    while(pt>=l&&v[0][seq[pt]]>=v[0][seq[i]]){
      Add1(h[0][seq[pt]],f[0][seq[pt]],g[0][seq[pt]]);
      --pt;
    }
    int tmpmax=qMax1(h[0][seq[i]]);
    if(tmpmax+1>f[0][seq[i]]){
      f[0][seq[i]]=tmpmax+1;g[0][seq[i]]=qSum1(h[0][seq[i]],tmpmax);
    }else if(tmpmax+1==f[0][seq[i]]){
      g[0][seq[i]]+=qSum1(h[0][seq[i]],tmpmax);
    }
  }
  for(int i=mid;i>pt;--i)del1(h[0][seq[i]]);
  cdq1(mid+1,r);
  sort(seq+l,seq+r+1,cmp2);
}
bool cmp3(const int &a,const int &b){
  return v[1][a]<v[1][b];
}
void cdq2(int l,int r){
  if(l==r)return;
  int mid=(l+r)>>1;
  cdq2(l,mid);
  for(int i=l;i<=r;++i)seq[i]=i;
  sort(seq+mid+1,seq+r+1,cmp3);
  sort(seq+l,seq+mid+1,cmp3);
  int pt=l;
  for(int i=mid+1;i<=r;++i){
    while(pt<=mid&&v[1][seq[pt]]<=v[1][seq[i]]){
      Add2(h[1][seq[pt]],f[1][seq[pt]],g[1][seq[pt]]);
      ++pt;
    }
    int tmpmax=qMax2(h[1][seq[i]]);
    if(tmpmax+1>f[1][seq[i]]){
      f[1][seq[i]]=tmpmax+1;g[1][seq[i]]=qSum2(h[1][seq[i]],tmpmax);
    }else if(tmpmax+1==f[1][seq[i]]){
      g[1][seq[i]]+=qSum2(h[1][seq[i]],tmpmax);
    }
  }
  for(int i=l;i<pt;++i)del2(h[1][seq[i]]);
  cdq2(mid+1,r);
  sort(seq+l,seq+r+1,cmp3);
}
int main(){
  int n;scanf("%d",&n);
  for(int i=1;i<=n;++i)scanf("%d%d",&h[0][i],&v[0][i]);
  for(int i=1;i<=n;++i)seq[i]=i;
  sort(seq+1,seq+n+1,cmp1);
  int tot=0,old=-1;
  for(int i=1;i<=n;++i){
    if(old!=h[0][seq[i]]){
      old=h[0][seq[i]];++tot;
    }
    h[0][seq[i]]=tot;
  }
  for(int i=1;i<=n;++i){
    h[1][i]=h[0][n-i+1];v[1][i]=v[0][n-i+1];
  }
  for(int i=1;i<=n;++i)seq[i]=i;
  for(int i=1;i<=n;++i)f[0][i]=f[1][i]=g[0][i]=g[1][i]=1;
  cdq1(1,n);cdq2(1,n);
  int ans=0;
  for(int i=1;i<=n;++i){
    if(f[0][i]>ans)ans=f[0][i];
  }
  printf("%d\n",ans);
  ll sum=0;
  for(int i=1;i<=n;++i){
    if(f[0][i]==ans)sum+=g[0][i];
  }
  for(int i=1;i<=n;++i){
    if(f[0][i]+f[1][n-i+1]-1!=ans)printf("%.8f ",0.0);
    else printf("%.8f ",double(g[0][i]*g[1][n-i+1]/(sum)));
  }
  return 0;
}

 

posted @ 2017-02-19 16:44  liu_runda  阅读(807)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难