JZOJ 5585 老夫

          • 传送门
          • 考场上的思路
          • 正解
          • 参考代码

传送门
考场上的思路

  首先按照 b 排序,然后就相当于是考虑 a 在排序后组成的最大矩形(差不多那个意思)。发现加入一个数就等价于将小于它的所有数对应的答案加上它们自己,自然地想到了每个结点带有权重的线段树,却发现这种线段树只适合于求和,不适合于取最大值。也想到过分块和单调数据结构,但是失败了……

正解

  我们维护 p=pi p = p i 时的最大收益。这样,插入一个数,就等于将小于等于它的数加上它们本身这么多的贡献。现在的问题是,如何找出最大值。

  可以使用分块。当我们对一整块进行操作时,就给这个块的标记加 1,对于剩下的,暴力维护即可。怎么计算整块的最大值呢?假设目前整块中的某个位置的值为 val[i],那么它实际上的值应该是 vai[i] + i * tag[inBlock[i]]。发现,所有的值都可以写成如下形式:

vali=ki+vali v a l i = k ⋅ i + v a l ′ i

  考虑使用单调队列维护这个东西。当 tag 增加时,我们弹出队首的更劣解。但是怎么构建这个单调队列呢?我们只最在暴力重构时构建它,这里需要用到一些斜率优化的方法使得解正确。

  设 i<j<k i < j < k 且在同一块中,若 j j i i 更优,则有:

vali+itag<valj+jtag v a l i + i ⋅ t a g < v a l j + j ⋅ t a g

  即:

valivaljji<tag v a l i − v a l j j − i < t a g

  当 valivaljji>valjvalkkj v a l i − v a l j j − i > v a l j − v a l k k − j 时, j j 永远不会是最优值(斜率优化),这时弹出队尾即可。

参考代码
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{
    INT_PUT a = 0;
    bool positive = true;
    char ch = getchar();
    while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
    if (ch == '-')
    {
        positive = false;
        ch = getchar();
    }
    while (std::isdigit(ch))
    {
        a = a * 10 - (ch - '0');
        ch = getchar();
    }
    if (positive) a = -a;
    return a;
}
void printOut(INT_PUT x)
{
    char buffer[20];
    int length = 0;
    if (x < 0) putchar('-');
    else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
    putchar(' ');
}

const int maxn = int(1e5) + 5;
int n, w;
struct People
{
    int a;
    int b;
    void read()
    {
        a = readIn();
        b = readIn();
    }
    bool operator<(const People& y) const
    {
        return b < y.b;
    }
} people[maxn];
int maxa, maxb;

#define RunInstance(x) delete new x
struct brute
{
    brute()
    {
        std::sort(people + 1, people + 1 + n);
        int cnt = 1;
        std::multiset<int> A;
        for (int c = 1, to = maxb + 1; c <= to; c++)
        {
            while (cnt <= n && c > people[cnt].b)
            {
                A.insert(people[cnt].a);
                cnt++;
            }
            LL ans = 0;
            int j = 0;
            int size = A.size();
            for (std::multiset<int>::iterator it = A.begin(); it != A.end(); it++)
                ans = std::max(ans, (LL)(size - (j++)) * *it);
            ans += (LL)(n - cnt + 1) * c * w;
            printOut(ans);
        }
    }
};
struct work
{
    int inBlock[maxn];
    int lBegin[maxn];
    int rEnd[maxn];
    int sqrtN;
    int N;
    void initBlocks()
    {
        sqrtN = std::sqrt(maxa);
        N = maxa / sqrtN + 1;
        for (int i = 0; i <= maxa; i++)
        {
            int t = inBlock[i] = i / sqrtN;
            if (!lBegin[t]) lBegin[t] = i;
            rEnd[t] = i;
        }
    }

    static const int maxN = 455;
    std::deque<int> mono[maxN];
    int tag[maxN];
    LL val[maxn];
    LL ans;

    void maintain(int v)
    {
        int block = inBlock[v];
        for (int i = 0; i < block; i++)
        {
            tag[i]++;
            std::deque<int>& q = mono[i];
            while (q.size() >= 2 && val[q.front()] + (LL)q.front() * tag[i] <= val[q[1]] + (LL)q[1] * tag[i])
                q.pop_front();
            if (q.size())
                ans = std::max(ans, val[q.front()] + (LL)q.front() * tag[i]);
        }
        for (int i = lBegin[block], to = rEnd[block]; i <= to; i++)
            val[i] += (LL)i * tag[block];
        tag[block] = 0;
        for (int i = lBegin[block]; i <= v; i++)
            val[i] += i;

        std::deque<int>& q = mono[block];
        q.clear();
        for (int i = lBegin[block], to = rEnd[block]; i <= to; i++)
        {
            while (q.size() >= 2 &&
                (double)(val[q[q.size() - 2]] - val[q.back()]) / (q.back() - q[q.size() - 2]) > (double)(val[q.back()] - val[i]) / (i - q.back()))
                q.pop_back();
            q.push_back(i);
            ans = std::max(ans, val[i]);
        }
    }

    work() : lBegin(), tag(), val(), ans()
    {
        initBlocks();
        std::sort(people + 1, people + 1 + n);

        int cnt = 1;
        for (int c = 1, to = maxb + 1; c <= to; c++)
        {
            while (cnt <= n && c > people[cnt].b)
            {
                int a = people[cnt].a;
                cnt++;
                maintain(a);
            }
            printOut(ans + (LL)(n - cnt + 1) * c * w);
        }
    }
};

void run()
{
    n = readIn();
    w = readIn();
    for (int i = 1; i <= n; i++)
    {
        people[i].read();
        maxa = std::max(maxa, people[i].a);
        maxb = std::max(maxb, people[i].b);
    }

    if ((LL)n * maxb <= LL(1e8))
        RunInstance(brute);
    else
        RunInstance(work);
}

int main()
{
#ifndef LOCAL
    freopen("laofu.in", "r", stdin);
    freopen("laofu.out", "w", stdout);
#endif
    run();
    return 0;
}

  时间复杂度为 O(nn) O ( n n )

你可能感兴趣的:(OI,分块,斜率优化)