P9871 [NOIP2023] 天天爱打卡 Solution

Description

有一个 01 序列 a = ( a 1 , a 2 , ⋯   , a n ) a=(a_1,a_2,\cdots,a_n) a=(a1,a2,,an),同时有 m m m 个三元组 ( x i , y i , v i ) (x_i,y_i,v_i) (xi,yi,vi).
定义 w ⁡ ( a ) = ( ∑ i = 1 m v i × [ ( ∑ j = x i − y i + 1 x i a j ) = y i ] ) − d × ( ∑ i = 1 n a i ) \operatorname{w}(a)=(\sum\limits_{i=1}^m v_i\times[(\sum\limits_{j=x_i-y_i+1}^{x_i}a_j) = y_i])-d\times(\sum\limits_{i=1}^n a_i) w(a)=(i=1mvi×[(j=xiyi+1xiaj)=yi])d×(i=1nai).
你需要确定这个序列,使得 w ⁡ ( a ) \operatorname{w}(a) w(a) 最大,且序列中没有超过 k k k 个连续的 1,求这个最大值, a a a 可以为全 0.
t t t 组测试数据.

Limitations

1 ≤ t ≤ 10 1 \le t \le 10 1t10
1 ≤ n , d , v i ≤ 1 0 9 1 \le n,d,v_i \le 10^9 1n,d,vi109
1 ≤ m ≤ 1 0 5 1 \le m \le 10^5 1m105
0 ≤ x i − y i < x i ≤ n 0 \le x_i-y_i < x_i \le n 0xiyi<xin
2 s , 512 MB 2\text{s},512\text{MB} 2s,512MB

Solution

显然要 DP.
下记 l i = x y − y i + 1 ,    r i = x i l_i=x_y-y_i+1,\;r_i=x_i li=xyyi+1,ri=xi.
f i f_i fi 为考虑 a 1 ⋯ i a_{1\cdots i} a1i a i = 1 a_i=1 ai=1 w ⁡ ( a ) \operatorname{w}(a) w(a) 最大值,并设 g i = max ⁡ j = 1 i f i g_i=\max\limits_{j=1}^i f_i gi=j=1maxifi.
易得 f i = max ⁡ j = i − k i { g j − 1 − ( i − j ) × d + ∑ [ l k , r k ] ⊆ ( j , i ] v p } f_i=\max\limits_{j=i-k}^i\{g_{j-1}-(i-j)\times d + \sum\limits_{[l_k,r_k]\subseteq (j,i]} v_p\} fi=j=ikmaxi{gj1(ij)×d+[lk,rk](j,i]vp}
显然可以用线段树维护每个决策对 w ⁡ ( a ) \operatorname{w}(a) w(a) 的影响 h i h_i hi,考虑 i − 1 i-1 i1 转移到 i i i h h h 如何变化.
首先 a j ⋯ i a_{j\cdots i} aji 中全为 1 1 1,所以对每个 j ∈ [ 0 , i ) j\in[0,i) j[0,i) 执行 h j ← h j − d h_j\gets h_j-d hjhjd.
这时满足 r k = i r_k=i rk=i 的三元组就可以被选择,所以对每个 r k = i , j ∈ [ 0 , l k ) r _k=i,j\in[0,l_k) rk=i,j[0,lk) 执行 h j ← h j + v k h_j\gets h_j+v_k hjhj+vk.

然后可得 f i = max ⁡ j = i − k i − 1 h j ,    g i = m a x ( g i − 1 , h i ) f_i=\max\limits_{j=i-k}^{i-1} h_j,\;g_i=max(g_{i-1},h_i) fi=j=ikmaxi1hj,gi=max(gi1,hi),发现 g i g_i gi j = i + 1 j=i+1 j=i+1 时可取到,故执行 h i + 1 ← h i + 1 + g i h_{i+1}\gets h_{i+1}+g_i hi+1hi+1+gi.

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).
离散化一下,只考虑连续段即可做到 O ( m log ⁡ m ) O(m\log m) O(mlogm).

Code

3.80 KB , 19.70 s , 42.75 MB    (in   total,   C++20   with   O2) 3.80\text{KB},19.70\text{s},42.75\text{MB}\;\texttt{(in total, C++20 with O2)} 3.80KB,19.70s,42.75MB(in total, C++20 with O2)

// Problem: P9871 [NOIP2023] 天天爱打卡
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P9871
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}

const i64 inf = 1e13;
struct Node {
    int l, r;
    i64 max, tag;
};
using Tree = vector<Node>;
inline int ls(int u) { return 2 * u + 1; }
inline int rs(int u) { return 2 * u + 2; }

inline void build(Tree& tr, int u, int l, int r) {
    tr[u].l = l;
    tr[u].r = r;
    tr[u].max = -inf;
    tr[u].tag = 0;
    if (l == r) return;
    const int mid = (l + r) >> 1;
    build(tr, ls(u), l, mid);
    build(tr, rs(u), mid + 1, r);
}
inline void pushup(Tree& tr, int u) {
    tr[u].max = max(tr[ls(u)].max, tr[rs(u)].max);
}
inline void apply(Tree& tr, int u, i64 val) {
    tr[u].max += val;
    tr[u].tag += val;
}
inline void pushdown(Tree& tr, int u) {
    if (tr[u].tag) {
        apply(tr, ls(u), tr[u].tag);
        apply(tr, rs(u), tr[u].tag);
        tr[u].tag = 0;
    }
}
inline void update(Tree& tr, int u, int l, int r, i64 val) {
    if (l > r) return;
    if (l <= tr[u].l && tr[u].r <= r) return apply(tr, u, val);
    const int mid = (tr[u].l + tr[u].r) >> 1;
    pushdown(tr, u);
    if (l <= mid) update(tr, ls(u), l, r, val);
    if (r > mid) update(tr, rs(u), l, r, val);
    pushup(tr, u);
}
inline i64 query(Tree& tr, int u, int l, int r) {
    if (l <= tr[u].l && tr[u].r <= r) return tr[u].max;
    const int mid = (tr[u].l + tr[u].r) >> 1;
    i64 res = -inf;
    pushdown(tr, u);
    if (l <= mid) res = max(res, query(tr, ls(u), l, r));
    if (r > mid) res = max(res, query(tr, rs(u), l, r));
    return res;
}

inline void solve() {
    int n, m, k, d;
    scanf("%d %d %d %d", &n, &m, &k, &d);
    
    vector<array<int, 3>> A(m);
    vector<int> nums{n - 1};
    for (auto& [l, r, v] : A) {
        int x, y;
        scanf("%d %d %d", &x, &y, &v);
        nums.push_back(l = x - y + 1);
        nums.push_back(r = x);
    }
    sort(nums.begin(), nums.end());
    nums.erase(unique(nums.begin(), nums.end()), nums.end());
    for (auto& [l, r, _] : A) {
        l = lower_bound(nums.begin(), nums.end(), l) - nums.begin();
        r = lower_bound(nums.begin(), nums.end(), r) - nums.begin();
    }
    sort(A.begin(), A.end(), [&](const array<int, 3>& a, const array<int, 3>& b) {
        return a[1] < b[1];
    });
    
    vector dp(nums.size(), array<i64, 2>{-inf, -inf});
    Tree tr(nums.size() << 2);
    build(tr, 0, 0, nums.size() - 1);
    update(tr, 0, 0, 0, inf);
    for (int i = 0, j = 0, p = 0; i < nums.size(); i++) {
        if (i > 0) update(tr, 0, 0, i - 1, -d * (nums[i] - nums[i - 1]));
        while (nums[i] - nums[j] + 1 > k) j++;
        while (p < m && A[p][1] == i) {
            update(tr, 0, 0, A[p][0], A[p][2]);
            p++;
        }
        dp[i][0] = max(0LL, i > 0 ? max(dp[i - 1][0], dp[i - 1][1]) : 0LL);
        dp[i][1] = query(tr, 0, j, i) - d;
        if (i + 1 < nums.size()) {
            if (nums[i + 1] - nums[i] == 1) update(tr, 0, i + 1, i + 1, inf + dp[i][0]);
            else update(tr, 0, i + 1, i + 1, inf + max(dp[i][0], dp[i][1]));
        }
    }
    printf("%lld\n", max(dp.back()[0], dp.back()[1]));
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int c, t;
	scanf("%d %d", &c, &t);
	while (t--) solve();
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值