[AtCoder Beginner Contest 158] - F - Removing Robots (线段树+DP)

Problem Statement

There are NN robots numbered 11 to NN placed on a number line. Robot ii is placed at coordinate XiXi. When activated, it will travel the distance of DiDi in the positive direction, and then it will be removed from the number line. All the robots move at the same speed, and their sizes are ignorable.

Takahashi, who is a mischievous boy, can do the following operation any number of times (possibly zero) as long as there is a robot remaining on the number line.

  • Choose a robot and activate it. This operation cannot be done when there is a robot moving.

While Robot ii is moving, if it touches another robot jj that is remaining in the range [Xi,Xi+Di)[Xi,Xi+Di) on the number line, Robot jj also gets activated and starts moving. This process is repeated recursively.

How many possible sets of robots remaining on the number line are there after Takahashi does the operation some number of times? Compute this count modulo 998244353998244353, since it can be enormous.

题意:

给你n个机器人,每一个机器人都有2个参数,\(X_i\)代表机器人在数轴上的位置,\(D_i\)代表机器人能向右走的距离。

如果你激活第i个机器人,那么坐标在\([X_i, X_i + D_i)\)的所有机器人会被自动激活,该操作是递归进行的。

你可以激活任意的一些机器人(可能为0个,可能为n个,等等),问最后没被激活的机器人方案书一共有多少种?

思路:

我们应该知道,只要选择去手动激活机器人的方案不变,激活的顺序是无所谓的。

于是我们将机器人按照坐标轴升序排序,从最后一个机器人开始遍历处理:

维护一个数组\(R_i\)代表排序后的第i个机器人最多能激活到哪个机器人。

维护方法:

建立单点更新,区间查询最大值的线段树,初始\(tree[i]=i\)

用二分找到满足坐标在\([X_i, X_i + D_i)\)的最右边的机器人下标\(pos\)

那么\(R[i]=max(R_{i+1},\dots,R_{pos})\),此处用线段树进行区间查询最大值,

然后单点更新\(tree[i]=R[i]\)

然后考虑计算答案:

\(dp_i\)代表只考虑\(i,i+1,\dots,n\)这些机器人,一共有多少种方案数。

我们知道对于第i个机器人有2中选择:

1 、不手动激活它有\(dp_{i+1}\)种方案

2、手动激活它有\(dp_{R_i+1}\)钟方案

于是我们得到了转移方程:

\(dp[i] = dp[i + 1] + dp[R[i] + 1];\)

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#include <sstream>
#include <bitset>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
#define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define du2(a,b) scanf("%d %d",&(a),&(b))
#define du1(a) scanf("%d",&(a));
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) { if (a == 0ll) {return 0ll;} a %= MOD; ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
ll poww(ll a, ll b) { if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a ;} a = a * a ; b >>= 1;} return ans;}
void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
inline long long readll() {long long tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
inline int readint() {int tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
const int maxn = 300010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
int n;
pii a[maxn];
int b[maxn];
struct node
{
    int l, r, v;
} segment_tree[maxn << 2];
void pushup(int rt)
{
    segment_tree[rt].v = max(segment_tree[rt << 1].v, segment_tree[rt << 1 | 1].v);
}
void build(int rt, int l, int r)
{
    segment_tree[rt].l = l;
    segment_tree[rt].r = r;
    if (l == r)
    {
        segment_tree[rt].v = l;
        return ;
    }
    int mid = (l + r) >> 1;
    build(rt << 1, l, mid);
    build(rt << 1 | 1, mid + 1, r);
    pushup(rt);
}
void modify(int rt, int key, int val)
{
    if (segment_tree[rt].l == segment_tree[rt].r)
    {
        segment_tree[rt].v = val;
        return ;
    }
    int mid = (segment_tree[rt].l + segment_tree[rt].r) >> 1;
    if (key > mid)
    {
        modify(rt << 1 | 1, key, val);
    } else
    {
        modify(rt << 1, key, val);
    }
    pushup(rt);
}
int ask(int rt , int l, int r)
{
    if (segment_tree[rt].l >= l && segment_tree[rt].r <= r)
    {
        return segment_tree[rt].v;
    }
    int res = 0;
    int mid(segment_tree[rt].l + segment_tree[rt].r >> 1);
    if (l <= mid)
    {
        res = max(res, ask(rt << 1, l, r));
    }
    if (r > mid)
    {
        res = max(res, ask(rt << 1 | 1, l, r));
    }
    return res;
}
int R[maxn];
ll dp[maxn];
const ll mod = 998244353ll;
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    n = readint();
    repd(i, 1, n)
    {
        a[i].fi = readint();
        a[i].se = readint();
    }
    sort(a + 1, a + 1 + n);
    repd(i, 1, n)
    {
        b[i] = a[i].fi;
    }
    build(1, 1, n);
    dp[n + 1] = 1ll;
    for (int i = n; i >= 1; --i)
    {
        int pos = upper_bound(b + 1, b + 1 + n, a[i].fi + a[i].se - 1) - b;
        R[i] = ask(1, i, pos - 1);
        modify(1, i, R[i]);
        dp[i] = dp[i + 1] + dp[R[i] + 1];
        dp[i] %= mod;
    }
    printf("%lld\n", dp[1] );
    return 0;
}