• #10470. 「2020-10-02 提高模拟赛」流水线 (line)


    题面:#10470. 「2020-10-02 提高模拟赛」流水线 (line)

    题目中的那么多区间的条件让人感觉极其难以维护,而且贪心的做法感觉大多都能 hack 掉,因此考虑寻找一些性质,然后再设计 DP 状态。

    设两端区间(Q_i)(Q_j)满足(Q_i subseteq Q_j),那么显然(Q_j)要么单独一组,要么就和(Q_i)一组。

    证明使用反证法,设(Q_j)与其他某些一组,那么我把(Q_j)放入(Q_i)那一组,显然两组的答案都不会变少。

    因此我们认为(Q_j)这一段无用了,当且仅当它单独一组时我们再计算它的贡献(t_j-s_j)。剩下的区间显然满足性质:左端点与右端点分别递增,于是就可以 DP 了。设(f_{i,j})为前(i)个区间分成了(j)组后最大的收获,状态转移方程:

    [f_{i,j}==min_{k<i,t_{k+1}>s_i}{f_{k,j-1}+t_{k+1}-s_i} ]

    正确性来源于这些区间的并就等于([s_i,t_{k+1}]),使用单调队列优化可以实现(Theta(n^2))。最后枚举一下我选几个之前不要的那种大区间,从大到小枚举,就可以了。

    然后就不得不提这题实现的诸多细节了,因为我们要保证答案更新时一定是从合法的值更新,所以(f)数组的初值统统要设为负无穷,可以避免非常多的细节,还有f[0][0]=0那一句其实是最妙的,能够解决很多初值的问题。

    memset(f, -127 / 3, sizeof f);
    f[0][0] = 0;
    for (int i = 1; i <= k; i++)
    {
    	q[head = tail = 1] = i - 1;
    	for (int j = i; j <= cnt; j++)
    	{
    		while (t[nw[q[head] + 1]] <= s[nw[j]] && head <= tail)
    		{
    			head++;
    		}
    		f[j][i] = f[q[head]][i - 1] + t[nw[q[head] + 1]] - s[nw[j]];
    		while (head <= tail && f[j][i - 1] + t[nw[j + 1]] >= f[q[tail]][i - 1] + t[nw[q[tail] + 1]])
    		{
    			tail--;
    		}
    		q[++tail] = j;
    	}
    }
    LL ans = 0, now = 0;
    for (int i = 0; i <= k; i++)
    {
    	if (f[cnt][k - i])
    		ans = max(ans, now + f[cnt][k - i]);
    	if (hp.empty()) break;
    	now += hp.top(); hp.pop();
    }
    
    as 0.4123
  • 相关阅读:
    Netbeans C++ unable to resolve identifier 无法解析标识符
    Netbeans C++ unable to resolve identifier 无法解析标识符
    代码版本《1》
    Perl 列表框
    Perl 子程序变量外部使用--又称为闭包
    标签和单行文本
    Linux显示使用者将不能利用交谈式指令来对行程
    Linux显示更新十次后退出
    Linux显示进程状态
    Linux显示服务器完整的状态信息
  • 原文地址:https://www.cnblogs.com/Linshey/p/13873166.html
Copyright © 2020-2023  润新知