洛谷 P11470 昆明之泪(dp、背包)

P11470 昆明之泪

题目描述

给定一串长度为 n n n 的数对序列 ( x i , y i ) (x_i,y_i) (xi,yi),其中 x i , y i x_i, y_i xi,yi 都是整数。

m m m 次询问,每次给定一个两个整数 a , b a, b a,b,你需要先选定一个整数 k k k(注意 k k k 可以为 0 0 0),然后再选定一个正整数序列 1 ≤ p 1 < p 2 < ⋯ < p k ≤ n 1 \le p_1 < p_2 < \cdots < p_k \le n 1p1<p2<<pkn(若 k = 0 k = 0 k=0 则该序列为空),使得
min ⁡ ( a + ∑ i = 1 k x p i , b + ∑ i = 1 k y p i ) \min\left(a + \sum\limits_{i = 1}^{k} x_{p_i} , b + \sum\limits_{i = 1}^{k} y_{p_i}\right) min(a+i=1kxpi,b+i=1kypi)

最大,输出这个最大值。

输入格式

第一行一个整数 n n n

接下来 n n n 行,每行两个整数代表 x i , y i x_i, y_i xi,yi

接下来一行一个整数 m m m

接下来 m m m 行,每行两个整数代表每次询问的 a , b a, b a,b

输出格式

输出 m m m 行。

一行一个数字,代表符合题意的最大值。

样例 #1

样例输入 #1

2
2 -3
3 -2
1
1 6

样例输出 #1

4

提示

1 ≤ n ≤ 1 0 3 1\le n \le 10^3 1n103 0 ≤ ∑ ∣ x i ∣ ≤ 1 0 5 0\le \sum \lvert x_i\rvert \le 10^5 0xi105 0 ≤ ∣ y i ∣ ≤ 1 0 12 0\le \lvert y_i \rvert \le 10^{12} 0yi1012 1 ≤ m ≤ 2 × 1 0 5 1\le m \le 2\times 10^5 1m2×105 0 ≤ ∣ a ∣ , ∣ b ∣ ≤ 1 0 12 0\le \lvert a\rvert, \lvert b\rvert \le 10^{12} 0a,b1012

思路:

由数据范围发现 ∑ x i \sum x_i xi 很小,可以考虑背包dp求得 f [ i ] f[i] f[i] ∑ x = i \sum x = i x=i 时, ∑ y \sum y y 的最大值,然后给 f f f 数组做一个后缀 max,就能得到 “ 凑出 ∑ x ≥ i \sum x \ge i xi 的最大的 ∑ y \sum y y ”;
对于 x < 0 x<0 x<0时的处理:先将数对加入到贡献中,再取 x 、 y x、y xy为相反数,即可转化为 x > 0 x>0 x>0的情况;
最后可以发现 y = min ⁡ ( a + x , b + f [ x ] ) y = \min(a+x,b+f[x]) y=min(a+x,b+f[x])是一个单峰函数,询问时二分处理即可;

代码:
#include 
#define endl '\n'
#define int long long
typedef long long ll;
#define FU(i, a, b) for (int i = (a); i <= (b); ++i)
#define FD(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;

int x[1005], y[1005];
int f[100005];
int nx[1005], ny[1005];
const int N = 1e5;
signed main()
{
    for (int i = 0; i < 100005; i++)
    {
        f[i] = -1e18;
    }
    cin.tie(0)->ios::sync_with_stdio(0);
    int n, m;
    cin >> n;
    int ga = 0, gb = 0;
    int cnt = 0;

    for (int i = 0; i < n; i++)
    {
        cin >> x[i] >> y[i];
        if (x[i] >= 0 && y[i] >= 0) // x、y都大于0时总可以计入贡献
        {
            ga += x[i], gb += y[i];
            continue;
        }
        else if (x[i] < 0 && y[i] < 0) // x、y都小于0时一定不计入贡献
        {
            continue;
        }
        else if (x[i] < 0)
        { // 解决 x < 0 的情况
            ga += x[i], gb += y[i];
            nx[++cnt] = -x[i];
            ny[cnt] = -y[i];
            continue;
        }
        nx[++cnt] = x[i];
        ny[cnt] = y[i];
    }
    f[0] = 0;
    for (int i = 1; i <= cnt; i++)
    {
        for (int j = N; j >= nx[i]; j--)
        {
            f[j] = max(f[j], f[j - nx[i]] + ny[i]);
        }
    }

    for (int j = N ; j >= 0; j--)
    {
        f[j] = max(f[j + 1], f[j]);
    }

    cin >> m;
    while (m--)
    {
        int a, b;
        cin >> a >> b;
        a += ga;
        b += gb;
        if (a >= b)
            cout << b << "\n";
        else
        {
            int l = 0, r = N, ans = min(a, b);
            while (l <= r)
            {
                int mid = (l + r) / 2;
                int nowx = a + mid, nowy = b + f[mid];
                if (nowx <= nowy)
                {
                    ans = max(ans, min(nowx, nowy));
                    l = mid + 1;
                }
                else
                {
                    r = mid - 1;
                }
            }
            cout << ans << "\n";
        }
    }

    return 0;
}

你可能感兴趣的:(题解/补题,c++,算法,数据结构,动态规划)