首页 > 其他分享 >P2487 拦截导弹 题解

P2487 拦截导弹 题解

时间:2023-06-05 13:34:46浏览次数:43  
标签:Node 拦截导弹 int 题解 序列 maxn CDQ P2487 Data

拦截导弹

题目大意

给定若干元素,每个元素有 \(3\) 个属性 \(t_i,h_i,v_i\),求出一个使得对于 \(\forall i,j,i>j\),\(t_i>t_j,h_i\le h_j,v_i\le v_j\) 均成立的最长的子序列 \(a\) 的长度。并计算每个元素在所有的可能的 \(a\) 方案中的出现概率。

思路分析

  • 先看第一个问题

按 \(t\) 排好序后容易发现其实就是一个二维的不上升子序列问题。

首先有一个 \(n^2\) 的 DP 方程:

\[f_i=\max(f_j)+1,j<i,h_j\ge h_i,v_j\ge v_i \]

其中 \(f_i\) 表示以 \(i\) 结尾的最长不上升子序列的长度。

但问题类似于三维偏序的形式,因此考虑用 CDQ 分治进行优化。

考虑 CDQ 分治的过程,发现只需要在归并时查询前缀最值即可。

具体的说。在 CDQ 分治时,先递归左半部分,再将左右部分分别按照 \(h\) 排序,维护左右双指针 \(j,i\),若 \(h_i\le h_j\),那么在树状数组的 \(v_j\) 位置加入 \(f_j\) 的贡献,否则通过树状数组中 \(v_i\) 的前缀最值更新 \(f_i\),最后清空树状数组,复原数组,递归右半部分。

  • 再看第二个问题

我们发现,一个元素的出现概率可以通过计数的方法计算。

具体的说,我们在维护 \(f_i\) 时顺便维护 \(g_i\),表示长度为 \(f_i\) 以 \(i\) 结尾的的不上升子序列的个数。

但这样还是不行,我们发现一个元素可能位于整个序列的最长不上升子序列的中间位置。因此,我们可以令 \(f'_i\) 表示以 \(i\) 开始的最长不上升子序列的长度,令 \(g'_i\) 表示长度为 \(f'_i\) 以 \(i\) 开始的的不上升子序列的个数。

那么这时就可以统计答案了,当 \(f_i+f'_i-1=\text{len}\) 时(\(\text{len}\) 表示整个序列的最长不上升子序列的长度),即这个元素可以组成整个序列的最长不上升子序列时,它出现的概率为:

\[\frac{g_ig'_i}{\text{sum}} \]

其中 \(\text{sum}\) 表示整个序列的最长不上升子序列的个数。

而 \(f'\) 和 \(g'\) 可以通过将数组逆序取反后再跑一遍一模一样的 CDQ 解决。

  • 其他

需要对 \(v\) 离散化。

记得开 double

代码

代码小卡了一下常,目前是最优解。

#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;
const int N=50100;

int n,m,in1,in2;
int Y[N],c[N];

#define getchar() (S==T&&(T=(S=B)+fread(B,1,1<<16,stdin),S==T)?EOF:*S++)
char B[1<<16],*S=B,*T=B;

inline int read(){
    int x=0;char ch=getchar();
    while(ch<'0'||'9'<ch) ch=getchar();
    while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}//整数快读

inline void dwrite(int x){
    if(!x){putchar('0');return ;}
    int bit[20],p=0,i;
    for (;x;x/=10) bit[++p]=x%10;
    for (i=p;i>0;--i) putchar(bit[i]+48);
}//整数快写

inline void write(double x){
    static int n=100000,k=5;
    int y=(int)(x*n)%n;x=(int)x;
    dwrite(x),putchar('.');
    int bit[8],p=0,i;
    for(;p<k;y/=10) bit[++p]=y%10;
    for(i=p;i>0;i--) putchar(bit[i]+48);
}//小数快写

struct Node{
    int x,y,id;
}a[N],b[N];

struct Data{
    int maxn;
    double cnt;
    Data operator + (const Data &b){
        Data c;
        c.maxn=max(maxn,b.maxn);
        c.cnt=((maxn==c.maxn)?cnt:0)+((b.maxn==c.maxn)?b.cnt:0);
        return c;//f 和 g 的结构题,以重载运算符的形式更新
    }
}f[N],f2[N],ans;

struct Tree_Array{
    Data a[N];
    int tt[N],times;//树状数组时间戳 O(1) 清空
    #define lowbit(x) ((x)&(-(x)))
    inline void add(int x,Data v){
        for(;x;x-=lowbit(x)){
            if(tt[x]==times) a[x]=a[x]+v;
            else a[x]=v,tt[x]=times;
        }
    }
    inline void clear(){times++;}
    inline Data ask(int x){
        Data ans=Data{0,0};
        for(;x<=m;x+=lowbit(x))
            if(tt[x]==times) ans=ans+a[x];
        return ans;
    }
}tree;

void CDQ(int l,int r){
    if(l==r){f[a[l].id]=f[a[l].id]+Data{1,1};return ;}//边界
    int mid=l+r>>1,j=l;
    CDQ(l,mid);
    sort(a+l,a+mid+1,[](Node a,Node b){return a.x>b.x;});
    sort(a+mid+1,a+r+1,[](Node a,Node b){return a.x>b.x;});//按 x 排序
    for(register int i=mid+1;i<=r;++i){
        while(j<=mid&&a[j].x>=a[i].x) tree.add(a[j].y,f[a[j].id]),j++;//加入树状数组
        Data temp=tree.ask(a[i].y);temp.maxn++;//统计答案
        f[a[i].id]=f[a[i].id]+temp;
    }
    tree.clear();
    int minn=n+1,maxn=0;//手写的桶排,按照 id 来排
    for(int i=mid+1;i<=r;++i){
        c[a[i].id]=i;
        minn=min(minn,a[i].id);
        maxn=max(maxn,a[i].id);
    }
    for(int i=minn;i<=maxn;++i)
        b[i]=a[c[i]];
    for(int i=mid+1;i<=r;++i)
        a[i]=b[i];
    CDQ(mid+1,r);
}

int main(){
    n=read();
    for(register int i=1;i<=n;++i){
        in1=read();in2=read();
        a[i]=Node{in1,in2,i};
        Y[i]=in2;
    }
    sort(Y+1,Y+n+1);
    m=unique(Y+1,Y+n+1)-Y-1;
    for(register int i=1;i<=n;++i)
        a[i].y=lower_bound(Y+1,Y+m+1,a[i].y)-Y;//离散化
    CDQ(1,n);
    for(register int i=1;i<=n;++i) ans=ans+f[i];//更新答案,只需要一边
    dwrite(ans.maxn);putchar('\n');
    for(register int i=1;i<=n;++i){//数组反转
        f2[i]=f[i];
        f[i]=Data{0,0};
        a[i].id=n-a[i].id+1;
        a[i].x=-a[i].x;
        a[i].y=m-a[i].y+1;
    }
    for(int i=1;i<=n;++i)//一样的桶排
        c[a[i].id]=i;
    for(int i=1;i<=n;++i)
        b[i]=a[c[i]];
    for(int i=1;i<=n;++i)
        a[i]=b[i];
    CDQ(1,n);
    reverse(f+1,f+n+1);
    for(register int i=1;i<=n;++i){
        if(f[i].maxn+f2[i].maxn-1==ans.maxn)
            write(1.0*f[i].cnt*f2[i].cnt/ans.cnt),putchar(' ');
        else putchar('0'),putchar(' ');//判定答案
    }
    return 0;
}

标签:Node,拦截导弹,int,题解,序列,maxn,CDQ,P2487,Data
From: https://www.cnblogs.com/TKXZ133/p/17457559.html

相关文章

  • P5012 水の数列 题解
    水の数列题目大意对于给定的数列\(a\),选择一个数\(x\),定义其得分为数列中所有小于等于\(x\)的数形成的若干个连续区间的平方和除以\(x\)所得到的数。现进行多次询问,每次询问给定两个数\(l,r\),要求出一个得分最大的\(x\),满足数列中所有小于等于\(x\)的数形成的若干个......
  • [ABC201D] Game in Momotetsu World 题解
    GameinMomotetsuWorld题目大意在一个\(n\timesm\)的网格中,存在红色和蓝色两种格子,红色格子用-表示,蓝色格子用+表示。现在Takahashi和Aoki要在这个网格中进行游戏,游戏的规则如下:初始时,有一枚棋子摆放在左上角,即\((1,1)\)的位置。两名玩家的分数均为\(0\)。......
  • P2048 超级钢琴 题解
    超级钢琴题目大意求出序列中长度在\([L,R]\)中的所有区间的区间和前\(k\)大的区间的区间和。思路分析暴力做法是把所有符合条件的区间扔进堆里,再弹出\(k\)个,时间复杂度\(O((n^2+k)\logn)\),可以拿到\(20\text{pts}\)的好成绩。但真的有必要全部加进去吗?不!我们设五......
  • P5445 路灯 题解
    路灯题目大意在\(n+1\)个站点之间有\(n\)盏路灯,给定\(0\)时刻所有路灯的亮灭情况,在接下来的\(q\)个时刻,每时刻会发生以下两种事件之一:切换某一盏路灯的亮灭。询问两点之间存在多少时刻使得两点之间的路灯全部亮起。思路分析一道不错的数据结构。首先分析题目......
  • Mysql 主从备份 Last_Errno: 1146 Last_Error: Error executing row event: 错误问题
    本人在做主从备份的时候发现了此问题! 1主数据库是已经把这个表删除了丛数据库也是没有备份这个表但是一直报这个错原因是bin-log日志有这个表 但是没记录到已经把这个表删除了 主从表同步实际从库是根据主库的bin-log二进制的SQL进行执行的 这是Mysql的一个BUG1......
  • erlang实现长连接管理问题解决
    具体参见:http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-1/1.    erlang进程增加了休眠特性hibernate,支持连接从20w->70w;如上面的文章里面所说,使用hibernate后支撑的长连接飞涨。2.    40w时系统出现异常。查看系统日志发现socket......
  • CF1818D 题解
    一、题目描述:给你一颗$n$个点,$m$条边的简单无向图,可能不连通。我们定义$鱼图$为满足以下条件的无向图:$包含恰好\1\个环,环上有\1\个特殊的结点\u\,u\除了连在环上的\2\条边外还正好有\2\条边连向不在此环上的结点。$求是否存$鱼图$。若存......
  • 题解
    原题请见题目链接1.暴力求解这是我们线下水题赛的T1这里为初学者提供一个思路,假设我们现在刚入OI,在考场上如何怎么优雅的避免保灵显然我们只要用一个\(O(n)\)的暴力去扫描就行了,但是直接向后扫\(i+1,i+2\)这样走很容易寄。因为你在\(i+1\)跳的时候很容易把后面跳过了......
  • 【题解】[ABC304F] Shift Table(容斥)
    【题解】[ABC304F]ShiftTable题目链接ABC304F题意概述Takahashi和Aoki将在接下来的\(N\)天里兼职工作。Takahashi这\(n\)天的出勤情况由字符串\(S\)表示,其中\(S\)的第\(i\)个字符是#则表示他在第\(i\)天工作,第\(i\)个字符是.表示他在第\(i\)天休息......
  • 题解:【AGC054D】 (ox)
    题目链接Larry76牛牛首先考虑没有ox怎么做,就是将括号序列调成合法。\(|S|\)不大直接模拟一遍,记录\(now\)表示一个前缀权值,当遇到一个(时\(+1\),遇到一个)时\(-1\),当\(now<0\)的时候说明序列不合法即)多了,暴力向后找到第一个(交换到当前的)前面。这样我们......