Solution -「P2487」拦截导弹

cirnovsky /

朴素的做法就是二元组 (ai,bi)(a_i,b_i) 序列的 LIS dp,同时维护 LIS 的数量。ii 可在 jj 决策的条件是 i>j,aj<ai,bj<bii>j,a_j<a_i,b_j<b_i(将时间视为下标)。

于是联想到使用 cdq dac 优化,使用树状数组 / 线段树支持单点合并 dp 值,最大值。

然后,,第二问要维护反转过来的 dp 值,再跑一遍就行了。

说几个坑点,在 dac 时 l=rl=r 给 dp 赋初值时,dp 在 ll 上的点值不一定是第一次被访问,所以不能简单的赋为 (1,1)(1,1),要取 max(对着 xtw 的代码灵魂对照才找出来 憨笑)。

因为 [l,mid][l,mid] 的 dp 决策是对 (mid,r](mid,r] 的决策有影响的,所以不能先把左右两边跑完再计算跨区间贡献,而是应该先把 [l,mid][l,mid] 跑完,然后再跑右区间。这又带来一个细节,对左右区间的以 aia_i 为关键字的排序会使得原区间的 tit_i(下标)无序,需要在递归之前排回来。

#include <bits/stdc++.h>
using namespace std;
#define cmin(x, y) x = min(x, y)
#define cmax(x, y) x = max(x, y)
#define fors(i, l, r, ...) for(int i = (l), REP##i = (r), ##__VA_ARGS__; i <= REP##i; ++i)
#define dfors(i, r, l, ...) for(int i = (r), REP##i = (l), ##__VA_ARGS__; i >= REP##i; --i)
int f1[50100], f2[50100];
double g1[50100], g2[50100];
int n, _n;
struct request {
    int t, x, y;
} req[50100], tmp[50100];
struct S {
    int x = 0;
    double p = 0;
} bit[50100];
void merge(S& x, const S& y) {
    if(x.x > y.x) x = x;
    else if(x.x < y.x) x = y;
    else x = (S){x.x, x.p+y.p};
}
void add(int x, const S& v) {
    for(; x; x -= x&-x) merge(bit[x], v);
}
void reset(int x) {
    for(; x; x -= x&-x) bit[x] = (S){0, 0};
}
S ask(int x) {
    S res;
    for(; x <= _n; x += x&-x) merge(res, bit[x]);
    return res;
}
void solve(const int l, const int r, int f[], double g[]) {
    if(l == r) {
        cmax(f[req[l].t], 1), g[req[l].t] += f[req[l].t] == 1;
        return;
    }
    int mid = (l+r)>>1;
    sort(req+l, req+r+1,
        [](const request& lhs, const request& rhs) { return lhs.t < rhs.t; });
    solve(l, mid, f, g);
    sort(req+l, req+mid+1,
        [](const request& lhs, const request& rhs) { return lhs.y > rhs.y; });
    sort(req+mid+1, req+r+1,
        [](const request& lhs, const request& rhs) { return lhs.y > rhs.y; });
    int j = l;
    fors(i, mid+1, r) {
        for(; j <= mid && req[j].y >= req[i].y; ++j) add(req[j].x, (S){f[req[j].t], g[req[j].t]});
        S ret = ask(req[i].x);
        if(ret.x == 0) continue;
        if(f[req[i].t] < ret.x+1) f[req[i].t] = ret.x+1,g[req[i].t] = ret.p;
        else if(f[req[i].t] == ret.x+1) g[req[i].t] += ret.p;
    }
    fors(i, l, j) reset(req[i].x);
    solve(mid+1, r, f, g);
}
signed main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n;
    vector<int> v;
    fors(i, 1, n) {
        cin >> req[i].x >> req[i].y;
        req[i].t = i;
        v.emplace_back(req[i].x);
    }
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
    _n = static_cast<int>(v.size());
    fors(i, 1, n) req[i].x = lower_bound(v.begin(), v.end(), req[i].x)-v.begin()+1;
    solve(1, n, f1, g1);
    int max_f1 = *max_element(f1+1, f1+n+1);
    double sum_g1 = 0;
    fors(i, 1, n) sum_g1 += (max_f1 == f1[i])*g1[i];
    fors(i, 1, n) req[i].t = n-req[i].t+1,req[i].x = _n-req[i].x+1,req[i].y *= -1;
    solve(1, n, f2, g2);
    reverse(f2+1, f2+n+1);
    reverse(g2+1, g2+n+1);
    cout << max_f1 << "\n";
    fors(i, 1, n) cout << (f1[i]+f2[i]-1 == max_f1 ? g1[i]*g2[i]/sum_g1 : 0.0) << " \n"[i == n];
    return 0;
}