C100 CDQ 分治+树状数组 P2487 [SDOI2011] 拦截导弹

视频链接:C100 CDQ 分治+树状数组 P2487 [SDOI2011] 拦截导弹_哔哩哔哩_bilibili

 

 

 

 

 

Luogu P2487 [SDOI2011] 拦截导弹

// CDQ分治(归并排序)+树状数组 O(nlognlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

#define lowbit(x) (x&-x)
#define mid (l+r>>1)
const int N=50005;

// f1[i]表示以i结尾的最长非升子序列的长度
// g1[i]表示以i结尾的最长非升子序列的方案数
// f2[i]表示以i开头的最长非升子序列的长度
// g2[i]表示以i开头的最长非升子序列的方案数
int n,f1[N],f2[N]; double g1[N],g2[N];
int f[N]; double g[N]; //树状数组
struct E{
  int x,y,z; //高度,速度,时间
  int f; double g; //长度,方案数
}a[N];

bool cmpx(E a,E b){ //按x降序
  if(a.x!=b.x) return a.x>b.x;
  if(a.y!=b.y) return a.y>b.y;
  return a.z<b.z;
}
bool cmpy(E a,E b){ //按y降序
  return a.y>b.y;
}
void change(int x,int m,double s){ //点修
  while(x<=n){
    if(f[x]<m) f[x]=m,g[x]=s;
    else if(f[x]==m) g[x]+=s;
    x+=lowbit(x);
  }
}
int query1(int x){ //查询长度
  int len=0;
  while(x){
    len=max(len,f[x]);
    x-=lowbit(x);
  }
  return len;
}
double query2(int x,int m){ //查询方案数
  double s=0;
  while(x){
    if(f[x]==m) s+=g[x];
    x-=lowbit(x);
  }
  return s;
}
void clear(int x){ //清空树状数组
  while(x<=n){
    f[x]=g[x]=0;
    x+=lowbit(x);
  }
}
void merge(int l,int r){ //计算左半对右半的贡献
  int i=l,j=mid+1;
  while(j<=r){
    while(i<=mid&&a[i].y>=a[j].y){
      change(a[i].z,a[i].f,a[i].g); //zi加入树状数组
      i++;
    }
    int m=query1(a[j].z);
    if(a[j].f<m+1){            //若能变长,则
      a[j].f=m+1;              //更新最大长度
      a[j].g=query2(a[j].z,m); //更新方案数
    }
    else if(a[j].f==m+1)       //若同样长,则
      a[j].g+=query2(a[j].z,m);//累计方案数
    j++;
  }
  for(j=l;j<i;j++) clear(a[j].z); //清空树状数组
}
void CDQ(int l,int r){ //CDQ分治(归并排序)
  if(l==r)return;     //下面是中序遍历做状态转移
  CDQ(l,mid);   //处理左半
  sort(a+l,a+mid+1,cmpy);
  sort(a+mid+1,a+r+1,cmpy); //按y降序
  merge(l,r);   //把左半对右半的贡献加到右半上
  sort(a+mid+1,a+r+1,cmpx); //把右半顺序恢复
  CDQ(mid+1,r); //处理右半
}
int main(){
  int maxh=0,maxv=0,len=0; double sum=0;
  scanf("%d",&n);
  for(int i=1;i<=n;i++){
    scanf("%d%d",&a[i].x,&a[i].y);
    maxh=max(maxh,a[i].x);
    maxv=max(maxv,a[i].y);
    a[i].f=1;a[i].g=1;a[i].z=i;
  }
  
  sort(a+1,a+n+1,cmpx); //按x降序
  CDQ(1,n);             //CDQ分治,计算f1,g1
  for(int i=1;i<=n;i++)
    f1[a[i].z]=a[i].f, g1[a[i].z]=a[i].g;
  for(int i=1;i<=n;i++)len=max(len,f1[i]);
  for(int i=1;i<=n;i++)if(f1[i]==len)sum+=g1[i];  
  printf("%d\n",len);

  for(int i=1;i<=n;i++){ //把a数组逆序
    a[i].z=n-a[i].z+1,
    a[i].x=maxh-a[i].x+1,
    a[i].y=maxv-a[i].y+1;
    a[i].f=1; a[i].g=1;
  }
  sort(a+1,a+n+1,cmpx); //按x降序
  CDQ(1,n);             //CDQ分治,计算f2,g2
  for(int i=1;i<=n;i++)
    f2[a[i].z]=a[i].f, g2[a[i].z]=a[i].g;
  for(int i=1;i<=n;i++) //计算概率
    printf("%.5lf ",f1[i]+f2[n-i+1]-1==len?g1[i]*g2[n-i+1]/sum:0);  
}

 

 

// CDQ分治(归并排序)+树状数组 O(nlognlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

#define lowbit(x) (x&-x)
#define mid (l+r>>1)
const int N=50005;
// f1[i]表示以i结尾的最长非升子序列的长度
// g1[i]表示以i结尾的最长非升子序列的个数
// f2[i]表示以i开头的最长非升子序列的长度
// g2[i]表示以i开头的最长非升子序列的个数
int n,f1[N],f2[N]; double g1[N],g2[N];
int f[N]; double g[N]; //树状数组
struct E{
  int x,y,z; //高度,速度,时间
  int f; double g; //长度,个数
}a[N];

bool cmpx(E a,E b){ //按x降序
  if(a.x!=b.x) return a.x>b.x;
  if(a.y!=b.y) return a.y>b.y;
  return a.z<b.z;
}
bool cmpy(E a,E b){ //按y降序
  return a.y>b.y;
}
void change(int x,int m,double s){ //点修
  while(x<=n){
    if(f[x]<m) f[x]=m,g[x]=s;
    else if(f[x]==m) g[x]+=s;
    x+=lowbit(x);
  }
}
int query1(int x){ //查询长度
  int len=0;
  while(x){
    len=max(len,f[x]);
    x-=lowbit(x);
  }
  return len;
}
double query2(int x,int m){ //查询个数
  double s=0;
  while(x){
    if(f[x]==m) s+=g[x];
    x-=lowbit(x);
  }
  return s;
}
void clear(int x){ //清空树状数组
  while(x<=n){
    f[x]=g[x]=0;
    x+=lowbit(x);
  }
}
void merge(int l,int r){ //把左半对右半的贡献加到右半上
  int i=l,j=mid+1;
  while(i<=mid&&j<=r){
    if(a[i].y>=a[j].y){
      change(a[i].z,a[i].f,a[i].g); //zi加入树状数组
      i++;
    }
    else{
      int m=query1(a[j].z);
      if(a[j].f<m+1){             //若能变长,则
        a[j].f=m+1;               //更新最大长度
        a[j].g=query2(a[j].z,m);  //更新个数
      }
      else if(a[j].f==m+1)        //若同样长,则
        a[j].g+=query2(a[j].z,m); //累计个数
      j++;
    }
  }
  while(i<=mid) change(a[i].z,a[i].f,a[i].g),i++;
  while(j<=r){
      int m=query1(a[j].z);
      if(a[j].f<m+1){
        a[j].f=m+1;
        a[j].g=query2(a[j].z,m);
      }
      else if(a[j].f==m+1)
        a[j].g+=query2(a[j].z,m);
      j++;
  }
  for(j=l;j<=mid;j++) clear(a[j].z); //清空树状数组
}
void CDQ(int l,int r){ //CDQ分治(归并排序)
  if(l==r)return;    //下面是中序遍历做状态转移
  CDQ(l,mid);   //处理左半
  sort(a+l,a+mid+1,cmpy);
  sort(a+mid+1,a+r+1,cmpy); //按y降序
  merge(l,r);   //把左半对右半的贡献加到右半上
  sort(a+mid+1,a+r+1,cmpx); //把右半顺序恢复
  CDQ(mid+1,r); //处理右半
}
int main(){
  int maxh=0,maxv=0,len=0; double sum=0;
  scanf("%d",&n);
  for(int i=1;i<=n;i++){
    scanf("%d%d",&a[i].x,&a[i].y);
    maxh=max(maxh,a[i].x);
    maxv=max(maxv,a[i].y);
    a[i].f=1;a[i].g=1;a[i].z=i;
  }
  
  sort(a+1,a+n+1,cmpx); //按x排序
  CDQ(1,n);             //CDQ分治,计算f1,g1
  for(int i=1;i<=n;i++)
    f1[a[i].z]=a[i].f, g1[a[i].z]=a[i].g;
  for(int i=1;i<=n;i++)len=max(len,f1[i]);
  for(int i=1;i<=n;i++)if(f1[i]==len)sum+=g1[i];  
  printf("%d\n",len);

  for(int i=1;i<=n;i++){ //把a数组逆序
    a[i].z=n-a[i].z+1,
    a[i].x=maxh-a[i].x+1,
    a[i].y=maxv-a[i].y+1;
    a[i].f=1; a[i].g=1;
  }
  sort(a+1,a+n+1,cmpx); //按x排序
  CDQ(1,n);             //CDQ分治,计算f2,g2
  for(int i=1;i<=n;i++)
    f2[a[i].z]=a[i].f, g2[a[i].z]=a[i].g;
  for(int i=1;i<=n;i++) //计算概率
    printf("%.5lf ",f1[i]+f2[n-i+1]-1==len?g1[i]*g2[n-i+1]/sum:0);  
}

  

posted @ 2024-03-13 19:30  董晓  阅读(159)  评论(0编辑  收藏  举报