BZOJ 2244: [SDOI2011]拦截导弹 DP+CDQ分治

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 2244: [SDOI2011]拦截导弹 DP+CDQ分治相关的知识,希望对你有一定的参考价值。

2244: [SDOI2011]拦截导弹


Description

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度、并且能够拦截任意速度的导弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

在不能拦截所有的导弹的情况下,我们当然要选择使国家损失最小、也就是拦截导弹的数量最多的方案。但是拦截导弹数量的最多的方案有可能有多个,如果有多个最优方案,那么我们会随机选取一个作为最终的拦截导弹行动蓝图。

我方间谍已经获取了所有敌军导弹的高度和速度,你的任务是计算出在执行上述决策时,每枚导弹被拦截掉的概率。

 

Input

第一行包含一个正整数n,表示敌军导弹数量;

下面 行按顺序给出了敌军所有导弹信息:

i+1行包含2个正整数hivi,分别表示第 枚导弹的高度和速度。

 

Output

输出包含两行。

第一行为一个正整数,表示最多能拦截掉的导弹数量;

第二行包含n个0到1之间的实数,第i个数字表示第i枚导弹被拦截掉的概率(你可以保留任意多位有效数字)。

 

Sample Input

4

3 30

4 40

6 60

3 30

Sample Output

2

0.33333 0.33333 0.33333 1.00000

【数据规模和约定】


对于100%的数据,1≤n≤5*104, 1≤hi ,vi≤109;

均匀分布着约30%的数据,所有vi均相等。

均匀分布着约50%的数据,满足1≤hi ,vi≤1000。

HINT

 

题解:

  因为精度的问题wa了一天

  设定dp[i][0/1] 表示正向和反向的LIS,f[i][0/1] 为其方案数

  继承是一个三维偏序,一维排序,二维CDQ,三维树状数组,一次解决

  下面是两份不同的代码

技术分享
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define ls i<<1
#define rs ls | 1
#define mid ((ll+rr)>>1)
#define pii pair<int,double>
#define MP make_pair
typedef long long LL;
typedef unsigned long long ULL;
const long long INF = 1e18+1LL;
const double pi = acos(-1.0);
const int N = 2e5+10, M = 1e3+20,inf = 2e9;
struct ss{
    int t,x,y;
    /*bool operator < (const ss &r) const {
        return x == r.x ? y<r.y : x < r.x;
    }*/
}a[N],t[N];


bool cmp1(ss s1,ss s2) {
    return s1.t < s2.t;
}

bool cmp2(ss s1,ss s2) {
    if(s1.x == s2.x) {
        if(s1.y == s2.y) return s1.t < s2.t;
        else return s1.y > s2.y;
    }
    return s1.x > s2.x;
}


vector<double > ans;
int dp[N][2],scc = 0,tag[N];
double f[N][2];
int n;
pair<int,double > C[N],C1[N];
void update(int x,pii now) {
    for(int i = x; i ; i -= i&-i) {
        if(now.first == 0)C[i] = MP(0,0);
        else {
            if(C[i].first < now.first) {
                C[i] = now;
            }
            else if(C[i].first == now.first) {
                C[i].second += now.second;
            }
        }

    }
}
pii ask(int x) {
    pii ret = MP(0,0);
    for(int i = x; i <= n; i += i&-i) {
        if(ret.first < C[i].first) {
            ret = C[i];
        }
        else if(ret.first == C[i].first){
            ret.second += C[i].second;
        }
    }
    return ret;
 }
void cdq(int ll,int rr,int p) {
    if(ll == rr) {
        if( f[ll][p] == 0) {
            dp[ll][p] = 1;
            f[ll][p] = 1;
        }
        return ;
    }
    sort(a+ll,a+rr+1,cmp1);
    cdq(ll,mid,p);
    scc++;
    sort(a+ll,a+rr+1,cmp2);
    for(int i = ll; i <= rr; ++i) {
        if(a[i].t <= mid) {
            update(a[i].y,MP(dp[a[i].t][p],f[a[i].t][p]));
        }
        else {
            pii now = ask(a[i].y);
            now.first += 1;
            if(now.second == 0) continue;
            if(now.first > dp[a[i].t][p]) {
                dp[a[i].t][p] = now.first;
                f[a[i].t][p] = now.second;
            }
            else if(now.first == dp[a[i].t][p]){
                f[a[i].t][p] += now.second;
            }
        }
    }
    for(int i = ll; i <= rr; ++i) {
        if(a[i].t <= mid) update(a[i].y,MP(0,0));
    }
    sort(a+ll,a+rr+1,cmp1);
    cdq(mid+1,rr,p);
}
int cnt[N],san[N],cnts;
int main() {
    scanf("%d",&n);
    int mxxx = 0;
    for(int i = 1; i <= n; ++i) {
        scanf("%d%d",&a[i].x,&a[i].y);
        san[i] = a[i].y;
        mxxx = max(mxxx,a[i].x);
        a[i].t = i;
    }

    sort(san+1,san+n+1);
    int SA = unique(san+1,san+n+1) - san - 1;
    for(int i = 1; i <= n; ++i)
        a[i].y = lower_bound(san+1,san+SA+1, a[i].y) - san;


    cdq(1,n,0);

    sort(a+1,a+n+1,cmp1);
    for(int i  =1; i <= n; ++i) {
         a[i].y = SA - a[i].y + 1;
         a[i].x = mxxx - a[i].x + 1;
    }
    for(int i = 1; i <= n/2; ++i) {
        swap(a[i].x,a[n - i + 1].x);
        swap(a[i].y,a[n - i + 1].y);
    }
    cdq(1,n,1);

     int ans = 0;
    for(int i = 1; i <= n; ++i) {
        ans = max(ans,(int)dp[i][0]);
    }
    printf("%d\n",ans);
    double sum = 0.0;
    for(int i = 1; i <= n; ++i) {
        if(dp[i][0] == ans) sum += (double)f[i][0] * f[n-i+1][1];
    }
    for(int i = 1; i <= n; ++i) {
        if(dp[i][0] + dp[n-i+1][1] - 1 == ans) {
            printf("%.5lf",(double)f[i][0] * f[n-i+1][1] / sum);
        }
        else printf("%.5lf",0.0);
        if(i == n) printf("\n");
        else printf(" ");
    }
    return 0;
}
View Code
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int N = 1e5+10, M = 1e3+20,inf = 2e9;

double f[N][2];
double dp[N][2];
int ch[N];
struct ss {
    int t,x,y;
}a[N],t1[N],t2[N];
int cmp(ss s1,ss s2) {
    return s1.x > s2.x;
}
double C[N];
double D[N];
double first;
double second;
void update(int x,double firs,double secon) {
    for(int i = x; i < N; i += i&(-i)) {
        if(C[i] < firs) {
            C[i] = firs;
            D[i] = secon;
        }
        else if(C[i] == firs) {
            D[i] += secon;
        }
    }
}

void ask(int x) {
    first = 0.0,second = 0.0;
    for(int i = x; i >= 1; i -= i&(-i)) {
        if(C[i] > first) {
            first = C[i];
            second = D[i];
        }
        else if(C[i] == first) {
            second += D[i];
        }
    }
}
void go(int x) {
    for(int i = x; i < N; i += i & (-i)) {
       C[i] = 0,D[i] = 0;
    }
}
void CDQ(int ll,int rr,int p) {
    if(ll == rr) {
        f[ll][p] = max(f[ll][p],1.0);
        dp[ll][p] = max(dp[ll][p],1.0);
        return ;
    }
    int mid = (ll+rr)>>1;
    CDQ(ll,mid,p);
    int acnt = 0, bcnt = 0, tot = 0;
    for(int i = ll; i <= mid; ++i) t1[++acnt] = a[i];
    for(int i = mid+1; i <= rr; ++i) t2[++bcnt] = a[i];
    sort(t1+1,t1+acnt+1,cmp);
    sort(t2+1,t2+bcnt+1,cmp);
    int pa = 1,pb = 1;
    while(pb <= bcnt) {
        while(pa <= acnt && t1[pa].x >= t2[pb].x) {
            update(t1[pa].y,dp[t1[pa].t][p],f[t1[pa].t][p]);
            ch[++tot] = t1[pa].y;
            pa++;
        }
        ask(t2[pb].y);
        //cout<<first<<" "<<second<<endl;
        int id = t2[pb].t;
        if(first + 1 > dp[id][p]) {
            dp[id][p] = first+1;
            f[id][p] = second;
        }
        else if(first + 1 == dp[id][p])
            f[id][p] += second;
        pb++;
    }
    for(int i = 1; i <= tot; ++i) go(ch[i]);
    CDQ(mid+1,rr,p);
}
int n,san[N],SA;
int main() {
    int mx = -1;
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d%d",&a[i].x,&a[i].y);
        san[i] = a[i].y;
        a[i].t = i;
        mx = max(a[i].x,mx);
    }
    sort(san+1,san+n+1);
    SA = unique(san+1,san+n+1) - san  - 1;
    for(int i = 1; i <= n; ++i)
        a[i].y = lower_bound(san+1,san+SA+1,a[i].y) - san;
    for(int i = 1; i <= n; ++i) {
        a[i].y = SA - a[i].y + 1;
    }
    CDQ(1,n,0);
    
      for(int i = 1; i <= n; ++i) {
        a[i].y = SA - a[i].y + 1;
        a[i].x = mx - a[i].x + 1;
    }
    reverse(a+1,a+n+1);
    for(int i = 1; i <= n; ++i) {
         a[i].t = i;
    }
    
    CDQ(1,n,1);

    int ans = 0;
    for(int i = 1; i <= n; ++i) {
        ans = max(ans,(int)dp[i][0]);
    }
    printf("%d\n",ans);
    double sum = 0.0;
    for(int i = 1; i <= n; ++i) {
        if(dp[i][0] == ans) sum += (double)f[i][0] * f[n-i+1][1];
    }
    for(int i = 1; i <= n; ++i) {
        if(dp[i][0] + dp[n-i+1][1] - 1 == ans) {
            printf("%.5lf",(double)f[i][0] * f[n-i+1][1] / sum);
        }
        else printf("%.5lf",0.0);
        if(i == n) printf("\n");
        else printf(" ");
    }
    return 0;
}

 

以上是关于BZOJ 2244: [SDOI2011]拦截导弹 DP+CDQ分治的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 2244: [SDOI2011]拦截导弹

[BZOJ2244][SDOI2011]拦截导弹

Bzoj2244: [SDOI2011]拦截导弹

BZOJ:2244: [SDOI2011]拦截导弹

bzoj2244: [SDOI2011]拦截导弹

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