2024暑期集训补题(线段树优化DP + 扫描线)

杭电多校4 1012

在这里插入图片描述
题意:
给定 n n n个点和 m m m个矩形,每个点有一个权值,从 ( 0 , 0 ) (0,0) (0,0)出发,只能向上和向右走,要求对于每个矩阵求出在不经过该矩阵的情况下,你能经过的最大的点权和。

题解:
在这里插入图片描述
考虑划分矩形区域,将答案分为四种情况讨论,如图, S 1 S1 S1为挑选的矩形区域
1.经过 S 3 , S 5 , S 7 S3,S5,S7 S3,S5,S7三个矩形达到右上角(必然经过 S 5 S5 S5中的至少一个点)
2.经过 S 2 , S 4 , S 6 S2,S4,S6 S2,S4,S6三个矩形达到右上角(必然经过 S 4 S4 S4中的至少一个点)
3.经过 S 3 , S 7 S3,S7 S3,S7两个矩形达到右上角(不经过任何 S 5 S5 S5中的点)
4.经过 S 2 , S 6 S2,S6 S2,S6两个矩形达到右上角(不经过任何 S 4 S4 S4中的点)
分类之后我们就可以考虑如何计算矩形内的贡献了
显然,我们需要计算三个值: f ( x ) : f(x): f(x) 0 0 0 x x x这个位置上的点的最大点权和, g ( x ) : g(x): g(x) x x x这个位置上的点到 n n n的最大点权和, h ( x ) : h(x): h(x)经过 x x x这个位置上的点的最大点权和。其中 h ( x ) = f ( x ) + g ( x ) − v x h(x) = f(x) + g(x) - v_x h(x)=f(x)+g(x)vx
这三个值通过线段树优化DP即可维护,而四种情况通过扫描线从左往右扫,扫四遍,第一遍扫 S 2 , S 3 S2,S3 S2,S3,第二遍扫 S 6 , S 7 S6,S7 S6,S7,并维护出 h ( x ) h(x) h(x),第三遍和第四遍通过维护出的 h ( x ) h(x) h(x) S 4 , S 5 S4,S5 S4,S5,最后取最值即可。
真别写线段树,会被卡常

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
// #define int long long
#define debug(p) for (auto i : p)cerr << i << " "; cerr << endl;
#define debugs(p) for (auto i : p)cerr << i.first << " " << i.second << endl;
typedef pair<int, int> pll;
string yes = "YES";
string no = "NO";
constexpr int N = 3e5 + 7;
struct Seg_tree{
    long long maxx;
}tr[N * 4];
struct Info{
    int xl, xr, yl, yr;
    long long lu, rd;
    long long ans; 
}info[N];
int p[N];
long long f[N], g[N], h[N], w[N];
vector<int>xl[N], xr[N];
inline void push_up(int u)
{
    tr[u].maxx = max(tr[u << 1].maxx, tr[u << 1 | 1].maxx);
}
void build(int u, int l, int r)
{
    if(l == r)
    {
        tr[u].maxx = w[l];
        return;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    push_up(u);
}
void modify(int u, int l, int r, int pos, long long x)
{
    if(l == r)
    {
        tr[u].maxx = x;
        return;
    }
    int mid = l + r >> 1;
    if(pos <= mid)modify(u << 1, l, mid, pos, x);
    else modify(u << 1 | 1, mid + 1, r, pos, x);
    push_up(u);
}
long long query(int u, int l, int r, int ql, int qr)
{
    if(ql > qr)return 0ll;
    if(ql <= l && r <= qr)return tr[u].maxx;
    int mid = l + r >> 1;
    long long res = 0ll;
    if(ql <= mid)res = max(res, query(u << 1, l, mid, ql, qr));
    if(qr > mid)res = max(res, query(u << 1 | 1, mid + 1, r, ql, qr));
    return res;
}
void init(int u, int l, int r)
{
    if(l == r)
    {
        tr[u].maxx = 0ll;
        return;
    }
    int mid = l + r >> 1;
    tr[u].maxx = 0ll;
    init(u << 1, l, mid), init(u << 1 | 1, mid + 1, r);
    // push_up(u);
}
void solve()
{
    long long n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> p[i] >> w[i];
        xl[i].clear();
        xr[i].clear();
    }
    for (int i = 1; i <= m; i++)
    {
        cin >> info[i].xl >> info[i].yl >> info[i].xr >> info[i].yr;
        xl[info[i].xl].push_back(i);
        xr[info[i].xr].push_back(i);
    }
    init(1, 1, n);
    for (int i = 1; i <= n; i++)
    {
        for (auto idx : xl[i])
        {
            info[idx].lu = query(1, 1, n, 1, info[idx].yr);
        }
        f[i] = query(1, 1, n, 1, p[i]) + w[i];
        modify(1, 1, n, p[i], f[i]);
        for (auto idx : xr[i])
        {
            info[idx].rd = query(1, 1, n, 1, info[idx].yl - 1);
        }
    }
    init(1, 1, n);
    for (int i = n; i >= 1; i--)
    {
        for (auto idx : xr[i])
        {
            info[idx].rd = info[idx].rd + query(1, 1, n, info[idx].yl, n);
        }
        g[i] = query(1, 1, n, p[i], n) + w[i];
        h[i] = f[i] + g[i] - w[i];
        modify(1, 1, n, p[i], g[i]);
        for (auto idx : xl[i])
        {
            info[idx].lu = info[idx].lu + query(1, 1, n, info[idx].yr + 1, n);
        }
    }
    init(1, 1, n);
    for (int i = 1; i <= m; i++)info[i].ans = max(info[i].lu , info[i].rd);
    for (int i = 1; i <= n; i++)
    {
        for (auto idx : xl[i])
        {
            info[idx].ans = max(info[idx].ans, query(1, 1, n, info[idx].yr, n));
        }
        modify(1, 1, n, p[i], h[i]);
    }
    init(1, 1, n);
    for (int i = n; i >= 1; i--)
    {
        for (auto idx : xr[i])
        {
            info[idx].ans = max(info[idx].ans, query(1, 1, n, 1, info[idx].yl));
        }
        modify(1, 1, n, p[i], h[i]);
    }
    init(1, 1, n);
    for (int i = 1; i <= m; i++)
    {
        cout << info[i].ans << endl;
    }

}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    int T = 1;
    cin >> T;
    while(T--)
    {
        solve();
    }
}
  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值