BZOJ 2244: [SDOI2011]拦截导弹 [CDQ分治 树状数组]

传送门

题意:三维最长不上升子序列以及每个元素出现在最长不上升子序列的概率



$1A$了好开心

首先需要从左右各求一遍,长度就是$F[0][i]+F[1][i]-1$,次数就是$G[0][i]*G[1][i]$

我们可以用一些转换来简化代码

反转之后变成$LIS$,然后再反转并且$x,y$取反还是$LIS$,写一遍就可以啦

然后本题的树状数组需要维护最大值以及最大值的数量,还有一个时间戳

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=5e4+5,INF=1e9;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}
int n,m;
int mp[N];
void iniMP(){
    sort(mp+1,mp+1+m);
    int p=0;
    mp[++p]=mp[1];
    for(int i=2;i<=m;i++) if(mp[i]!=mp[i-1]) mp[++p]=mp[i];
    m=p;
}
int Bin(int v){
    int l=1,r=m;
    while(l<=r){
        int mid=(l+r)>>1;
        if(v==mp[mid]) return mid;
        else if(v<mp[mid]) r=mid-1;
        else l=mid+1;
    }
    return 0;
}
int c[N],mark[N],CL;double cnt[N];
inline int lowbit(int x){return x&-x;}
inline void upd(int p,int f,double g){
    for(;p<=m;p+=lowbit(p)){
        if(mark[p]!=CL) mark[p]=CL,c[p]=f,cnt[p]=g;
        else if(c[p]<f) c[p]=f,cnt[p]=g;
        else if(c[p]==f) cnt[p]+=g;
    }
}
inline void que(int p,int &f,double &g){
    for(;p;p-=lowbit(p)) if(mark[p]==CL){
        if(c[p]>f)  f=c[p],g=cnt[p];
        else if(c[p]==f) g+=cnt[p];
    }
}
struct Data{
    int x,y,id;
}a[N];
int ref[N];
inline bool cmpX(int p,int q){
    return a[p].x==a[q].x ? a[p].id<a[q].id : a[p].x<a[q].x;
}
int F[2][N];double G[2][N];
void CDQ(int l,int r,int tp){
    if(l==r) return;
    int mid=(l+r)>>1;
    CDQ(l,mid,tp);
    for(int i=l;i<=r;i++) ref[i]=i;
    sort(ref+l,ref+r+1,cmpX);
    int *f=F[tp]; double *g=G[tp];
    CL++;
    for(int i=l;i<=r;i++){
        int _=i;i=ref[i];
        if(a[i].id<=mid) upd(a[i].y,f[a[i].id],g[a[i].id]);
        else{
            int v=0;double sum=0;que(a[i].y,v,sum);
            v++;
            if(v>f[a[i].id]) f[a[i].id]=v,g[a[i].id]=sum;
            else if(v==f[a[i].id]) g[a[i].id]+=sum;
        }
        i=_;
    }
    CDQ(mid+1,r,tp);
}
int main(){
    freopen("in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++) a[i].x=read(),mp[++m]=a[i].y=read();
    iniMP();
    for(int i=1;i<=n;i++) a[i].y=Bin(a[i].y);
    for(int i=1;i<=n;i++) F[0][i]=F[1][i]=G[0][i]=G[1][i]=1;
    reverse(a+1,a+1+n);
    for(int i=1;i<=n;i++) a[i].id=i;
    CDQ(1,n,0);
    //for(int i=1;i<=n;i++) printf("F0  %d %d %d  %d %lf\n",i,a[i].x,a[i].y,F[0][i],G[0][i]);
//puts("---");
    reverse(a+1,a+1+n);
    for(int i=1;i<=n;i++) a[i].y=m-a[i].y+1,a[i].x=INF-a[i].x,a[i].id=i;
    CDQ(1,n,1);
    //for(int i=1;i<=n;i++) printf("F1  %d %d %d  %d %lf\n",i,a[i].x,a[i].y,F[1][i],G[1][i]);

    for(int i=1;i<=n/2;i++) swap(F[0][i],F[0][n-i+1]),swap(G[0][i],G[0][n-i+1]);
    int ans=0;double tot=0;
    for(int i=1;i<=n;i++) ans=max(ans,F[0][i]);
    for(int i=1;i<=n;i++) if(F[0][i]==ans) tot+=G[0][i];//printf("tot %lf\n",tot);
    printf("%d\n",ans);
    for(int i=1;i<=n;i++){
        if(F[0][i]+F[1][i]-1!=ans) printf("%.5lf ",0.0);
        else printf("%.5lf ",G[0][i]*G[1][i]/tot);
    }
}
时间: 2024-12-21 09:13:46

BZOJ 2244: [SDOI2011]拦截导弹 [CDQ分治 树状数组]的相关文章

bzoj 2244: [SDOI2011]拦截导弹 cdq分治

2244: [SDOI2011]拦截导弹 Time Limit: 30 Sec  Memory Limit: 512 MBSec  Special JudgeSubmit: 237  Solved: 103[Submit][Status][Discuss] Description 某 国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度.并且能够拦截任意速度的导 弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度

BZOJ 2244 SDOI2011 拦截导弹 CDQ分治/二维树状数组

题目大意:给定一个序列,每个元素是一个二元组,等概率选择一LIS,求LIS长度以及每个元素被选中的概率 第一问CDQ分治裸上 第二问用每个元素所在的LIS个数/总LIS个数就是答案 每个元素所在的LIS自己必选,然后统计前面的方案数和后面的方案数 以前面的方案数为例,令f[x]为以x结尾的LIS长度,那么有DP方程: g[i]=Σg[j] (f[j]+1=f[i],j<i,a[j].x<a[i].x,a[j].y<a[i].y) 将所有元素按f值排序,分层DP,每层DP是一个三维偏序,上

BZOJ 2244 [SDOI2011]拦截导弹 ——CDQ分治

三维偏序,直接CDQ硬上. 正反两次CDQ统计结尾的方案数,最后统计即可. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=i;--i) #define

bzoj 2648 SJY摆棋子 cdq分治+树状数组

题面 题目传送门 解法 同bzoj2716 自己cdq写的还是丑啊,别人A掉了我T飞了 代码 #include <bits/stdc++.h> #define inf 1 << 30 #define N 1000010 using namespace std; template <typename node> void chkmax(node &x, node y) {x = max(x, y);} template <typename node>

BZOJ 2683 简单题 cdq分治+树状数组

题意:链接 **方法:**cdq分治+树状数组 解析: 首先对于这道题,看了范围之后,二维的数据结构是显然不能过的,于是我们可能会考虑把一维排序之后另一位上数据结构什么的,然而cdq分治却能够很好的体现它的作用. 首先,对于每一个询问求和,显然是x在它左边的并且出现时间在它之前的所有的change对他可能会有影响. 我们按照x第一关键字,y第二关键字,操作第三关键字来排序所有的询问,然后在cdq的时候,每次递归处理左半区间,按照x动态的将y这一列的值加到树状数组里,来更新右半边的所有询问,注意这

HDU 5618:Jam&#39;s problem again(CDQ分治+树状数组处理三维偏序)

http://acm.hdu.edu.cn/showproblem.php?pid=5618 题意:-- 思路:和NEUOJ那题一样的.重新写了遍理解了一下,算作处理三维偏序的模板了. 1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 using namespace std; 6 #define INF 0x3f3f3f3f 7 #d

XJOI NOIP2015模拟赛Day1 T2 ctps bitset优化 或 排序+cdq分治+树状数组+平衡树

题意: 4维空间中有1个点集A,|A|=n,用(a,b,c,d)表示每个点. 共有m个询问,每次询问输入一个点(a,b,c,d),求最大的S,其中S={p|p∈A且ap<=a,bp<=b,cp<=c,dp<=d},输出|S| 输入格式: 第一行n 接下来n行有n个4维点对 第n+2行有一个数m 再接下来m行每行有一个四维点对,表示每个询问 输出格式: 对于每个询问输出一个数 **方法:**bitset优化 或 排序+cdq分治+树状数组+平衡树 解析: 神题,考场不会,暴力骗40,

【BZOJ4553】[Tjoi2016&amp;Heoi2016]序列 cdq分治+树状数组

[BZOJ4553][Tjoi2016&Heoi2016]序列 Description 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一个值发生变化.现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可.注意:每种变化最多只有一个值发生变化.在样例输入1中,所有的变化是: 1 2 3 2 2 3 1 3 3 1

BZOJ2683: 简单题(CDQ分治 + 树状数组)

BZOJ2683: 简单题(CDQ分治 + 树状数组) 题意: 你有一个\(N*N\)的棋盘,每个格子内有一个整数,初始时的时候全部为\(0\),现在需要维护两种操作: 命令 参数限制 内容 \(1\ x\ y\ A\) \(1\le x,y \le N\),A是正整数 将格子\(x,y\)里的数字加上\(A\) \(2\ x1\ y1\ x2\ y2\) \(1\le x1\le x2\le N,1\le y1\le y2\le N\) 输出\(x1\ y1\ x2\ y2\)这个矩形内的数字