bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

http://www.lydsy.com/JudgeOnline/problem.php?id=2244

每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数

pre_len [i] 表示以i结尾的最长不下降子序列的长度

pre_sum[i] 表示对应长度下的方案数

suf_len[i] 表示以i开头的最长不下降子序列长度

suf_sum[i] 表示对应长度下的方案数

若已有了这4个数组

设最长上升子序列长度=mx

那么 如果pre_len[i]+suf_len[i] - 1==mx,那么第i枚导弹在最长上升子序列上

而且在pre_sum[i]*suf_sum[i] 个最长上升子序列上

如何获取这四个数组?

这是一个三维偏序问题,CDQ分治

若i后面可以接j

第一维:i的出现时间<j的出现时间

第二维:i的高度>=j的高度

第三维:i的速度>=j的速度

具体实现:

排序第一维,CDQ过程中排序解决第二维,树状数组解决第三维

对于第三维,要离散化

树状数组查询的是<=查询位置的信息,即用它来求最长不下降子序列更方便

所以在解决以i为开头的最长不上升子序列的时候

将第二维取负,第三维i变成n-i+1

在解决以i为开始的最长不上升子序列的时候

将第一维取负

对CDQ分治更进一步的理解:

之前写CDQ分治做数据结构题的时候,

写的是先解决左边对右边的贡献,在递归两边

但是这道题,要先递归左边,再解决左边对右边的贡献,再递归右边

因为最长不下降子序列 左边更新右边的时候,左边的所有不是等价的

即也需要用左边更新之后的结果来更新右边

而像那种修改查询题,左边的修改对左边查询的影响和对右边查询的影响是等价的

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

#define N 50001

#define lowbit(x) ( x&-x )

int n;
int h[N],v[N];
int tot,has[N];

struct node
{
    int x,y,z;
    int len;
    double sum;
}e[N];

int c[N];
double d[N];

int pre_len[N],suf_len[N];
double pre_sum[N],suf_sum[N];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); }
}

bool cmdy(node p,node q)
{
    if(p.y==q.y) return p.z<q.z;
    return p.y<q.y;
}

bool cmdx(node p,node q)
{
    return p.x<q.x;
}

void change(int pos,int len,double sum)
{
    while(pos<=n)
    {
        if(len>c[pos])
        {
            c[pos]=len;
            d[pos]=sum;
        }
        else if(len==c[pos]) d[pos]+=sum;
        pos+=lowbit(pos);
    }
}

int query_len(int pos)
{
    int ans=0;
    while(pos)
    {
        ans= ans>=c[pos] ? ans : c[pos];
        pos-=lowbit(pos);
    }
    return ans;
}

double query_sum(int pos,int val)
{
    double ans=0;
    while(pos)
    {
        if(c[pos]==val) ans+=d[pos];
        pos-=lowbit(pos);
    }
    return ans;
}

void clear(int pos)
{
    while(pos<=n)
    {
        c[pos]=d[pos]=0;
        pos+=lowbit(pos);
    }
}

void cdq(int l,int r)
{
    if(l==r) return;
    int mid=l+r>>1;
    cdq(l,mid);
    sort(e+l,e+mid+1,cmdy);
    sort(e+mid+1,e+r+1,cmdy);
    int i=l,j=mid+1;
    int len; double sum;
    for(;j<=r;++j)
    {
        while(i<=mid && e[i].y<=e[j].y)
        {
            change(e[i].z,e[i].len,e[i].sum);
            i++;
        }
        len=query_len(e[j].z)+1;
        sum=query_sum(e[j].z,len-1);
        if(len>e[j].len)
        {
            e[j].len=len;
            e[j].sum=sum;
        }
        else if(len==e[j].len) e[j].sum+=sum;
    }
    for(int k=l;k<=mid;++k) clear(e[k].z);
    sort(e+mid+1,e+r+1,cmdx);
    cdq(mid+1,r);
}

void work()
{
    sort(has+1,has+n+1);
    tot=unique(has+1,has+n+1)-has-1;
    for(int i=1;i<=n;++i) v[i]=lower_bound(has+1,has+tot+1,v[i])-has;
    for(int i=1;i<=n;++i)
    {
        e[i].x=i;
        e[i].y=-h[i];
        e[i].z=tot-v[i]+1;
        e[i].len=e[i].sum=1;
    }
    cdq(1,n);
    for(int i=1;i<=n;++i) pre_len[e[i].x]=e[i].len,pre_sum[e[i].x]=e[i].sum;
    for(int i=1;i<=n;++i)
    {
        e[i].x=-i;
        e[i].y=h[i];
        e[i].z=v[i];
        e[i].len=e[i].sum=1;
    }
    sort(e+1,e+n+1,cmdx);
    cdq(1,n);
    for(int i=1;i<=n;++i) suf_len[-e[i].x]=e[i].len,suf_sum[-e[i].x]=e[i].sum;
}

void get_ans()
{
    int mx=0;
    for(int i=1;i<=n;++i) mx=max(mx,pre_len[i]);
    printf("%d\n",mx);
    double cnt=0;
    for(int i=1;i<=n;++i)
        if(pre_len[i]==mx) cnt+=pre_sum[i];
    for(int i=1;i<=n;++i)
        if(pre_len[i]+suf_len[i]-1==mx) printf("%.5lf ",(double)pre_sum[i]*suf_sum[i]/cnt);
        else printf("0 ");
}

int main()
{
    read(n);
    for(int i=1;i<=n;++i)
    {
        read(h[i]); read(v[i]);
        has[i]=v[i];
    }
    work();
    get_ans();
}

原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8605562.html

时间: 2024-08-06 15:00:50

bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹的相关文章

bzoj千题计划246:bzoj2242: [SDOI2011]计算器

http://www.lydsy.com/JudgeOnline/problem.php?id=2242 #include<map> #include<cmath> #include<cstdio> using namespace std; int y,z,p; map<int,int>mp; int Pow(int a,int b,int m) { int ans=1; for(;b;a=1LL*a*a%m,b>>=1) if(b&1)

bzoj千题计划185:bzoj1260: [CQOI2007]涂色paint

http://www.lydsy.com/JudgeOnline/problem.php?id=1260 区间DP模型 dp[l][r] 表示涂完区间[l,r]所需的最少次数 从小到大们枚举区间[l,r] 如果col[l]==col[r] dp[l][r]=min(dp[l+1][r],dp[l][r-1],dp[l+1][r-1]+1) 否则 dp[l][r]=min(dp[l][k]+dp[k+1][r]) 我还是辣鸡啊~~~~(>_<)~~~~,这种题都不能秒 #include<c

bzoj千题计划304:bzoj3676: [Apio2014]回文串

https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 300001 char ss[N]; int s[N]; int tot=1,last; int fail[N],len

bzoj千题计划106:bzoj1014 [JSOI2008]火星人prefix

http://www.lydsy.com/JudgeOnline/problem.php?id=1014 两个后缀的最长公共前缀:二分+hash 带修改带插入:splay维护 #include<cstdio> #include<cstring> #include<iostream> #define L 100001 typedef unsigned long long ULL; using namespace std; char s[L+4]; int tot,root

bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic

http://www.lydsy.com/JudgeOnline/problem.php?id=1018 关键点在于只有两行 所以一个2*m矩形连通情况只有6种 编号即对应代码中的a数组 线段树维护 用b数组表示 节点第0/1行的最右一列是否连接了右边 来 辅助 节点的合并 查询 对两个点位于矩形的位置分4种情况讨论 两点是否联通,要考虑四种情况 (以两个位置是矩形左上角和右上角为例) 1.直接联通,线段树的节点包含了这种情况,直接判断 2. 3. 4. 后三种情况需要再查询[1,l]和[r,n

bzoj千题计划109:bzoj1019: [SHOI2008]汉诺塔

http://www.lydsy.com/JudgeOnline/problem.php?id=1019 题目中问步骤数,没说最少 可以大胆猜测移动方案唯一 (真的是唯一但不会证) 设f[i][j] 表示 从i号柱子 上把j个盘子移到 g[i][j] 柱子上的步数 初始化:f[0][1]=1,g[0][1] 根据优先级决定 设三根柱子分别为0,1,2 对于每一个f[x][i], 把前i-1个移走,把第i个移走,把前i-1个移回 令y=g[x][i-1],则k=0+1+2-x-y 我们希望 把i-

bzoj千题计划111:bzoj1021: [SHOI2008]Debt 循环的债务

http://www.lydsy.com/JudgeOnline/problem.php?id=1021 如果A收到了B的1张10元,那么A绝对不会把这张10元再给C 因为这样不如B直接给C优 由此可以推出 若A欠B20元,B欠C 30元, 那么A还C20元,B还C10元最优 所以一共只有 A->BC   B->AC  C->AB AB->C  BC->A  AC->B 这6种转移情况 根据输入,我们可以知道三人最终手中有多少钱ea.eb.ec,一共有多少钱sum 设f

bzoj千题计划112:bzoj1022: [SHOI2008]小约翰的游戏John

http://www.lydsy.com/JudgeOnline/problem.php?id=1022 http://www.cnblogs.com/TheRoadToTheGold/p/6744825.html #include<cstdio> #include<iostream> using namespace std; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar();

bzoj千题计划113:bzoj1023: [SHOI2008]cactus仙人掌图

http://www.lydsy.com/JudgeOnline/problem.php?id=1023 dp[x] 表示以x为端点的最长链 子节点与x不在同一个环上,那就是两条最长半链长度 子节点与x在同一个环上,环形DP,单调队列优化 对于每一个环,深度最小的那个点 有可能会更新 上层节点, 所以 每一个环DP完之后,更新 dp[深度最小的点] #include<cstdio> #include<iostream> #include<algorithm> using