C100 CDQ 分治+树状数组 P2487 [SDOI2011] 拦截导弹
视频链接:C100 CDQ 分治+树状数组 P2487 [SDOI2011] 拦截导弹_哔哩哔哩_bilibili
// 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); }