五一模拟赛 JZOJ 4883 灵知的太阳信仰 线性DP+Set

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/89764175

title

【问题描述】

在炽热的核熔炉中,居住着一位少女,名为灵乌路空。
据说,从来没有人敢踏入过那个熔炉,因为人们畏缩于空所持有的力量——核能。
核焰,可融真金。
每次核融的时候,空都会选取一些原子,排成一列。然后,她会将原子序列分成一些段,并将每段进行一次核融。
一个原子有两个属性:质子数和中子数。
每一段需要满足以下条件:
1、同种元素会发生相互排斥,因此,同一段中不能存在两个质子数相同的原子。
2、核融时,空需要对一段原子加以防护,防护罩的数值等于这段中最大的中子数。换句话说,如果这段原子的中子数最大为x,那么空需要付出x的代价建立防护罩。求核融整个原子序列的最小代价和。

【输入】

第一行一个正整数N,表示原子的个数。
接下来N行,每行两个正整数pi和ni,表示第i个原子的质子数和中子数。

【输出】

输出一行一个整数,表示最小代价和。

【输入输出样例1】

array.in
5
3 11
2 13
1 12
2 9
3 13
array.out
26

【数据范围】

对于20%的数据,1<=n<=100
对于40%的数据,1<=n<=1000
对于100%的数据,1<=n<=105,1<=pi<=n,1<=ni<=2*104

analysis

显然有 d p dp f [ i ] f[i] 为在 i i i + 1 i+1 之间切分最小代价和,那么 f [ n ] f[n] 就是答案。
f [ i ] = m a x ( f [ j ] + s [ j + 1 , i ] ) { j &gt; = l [ i ] } f[i]=max(f[j]+s[j+1,i]) \left\{j&gt;=l[i]\right\}

l [ i ] l[i] i i 点向左能到的位置。

举个例子:
5 4 7 6 2 5\quad 4 \quad7\quad 6\quad 2
现在 l [ i ] = 1 l[i]=1
发现能转移的为
f [ 4 ] + 2 f [ 3 ] + 6 f [ 0 ] + 7 { f [ 2 ] + 7 f [ 1 ] + 7 } f[4]+2\quad f[3]+6\quad f[0]+7 \left\{f[2]+7和f[1]+7不优\right\}

也就是以 x x 为该区间的代价的,左边界为下一个比它大的右边。
2 [ 5 , 5 ] , 6 [ 4 , 5 ] , 7 [ 1 , 5 ] 2的区间为[5,5],6的区间为[4,5],7的区间为[1,5]

现在我们到了下个 i i
5 4 7 6 2 5 5 \quad4 \quad7\quad 6\quad 2\quad 5

假设先在i能到达的l[i]=3,
现在转移变为
f [ 2 ] + 7 f [ 3 ] + 6 f [ 4 ] + 5 f[2]+7 \quad f[3]+6 \quad f[4]+5
显然只改变了前面的装移{因为l改变且递增},
把后面的 &lt; = 5 &lt;=5 的状态踢掉了,剩下的不变 f [ 3 ] + 6 f[3]+6

也就是说对于前一个状态,一些转移不变,也就是这部分的最大值已经算出,维护一下就 o k ok 了。
——Amber_lylovely

线段树写法:gmh77

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;

template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}

multiset<int>t;
int a[maxn],b[maxn];
int hou[maxn],l[maxn],data[maxn];
int sum[maxn],wei[maxn],f[maxn];
int main()
{
    freopen("array.in","r",stdin);
    freopen("array.out","w",stdout);
    int n;read(n);
    for (int i=1; i<=n; ++i) read(a[i]),read(b[i]);
    for (int i=1; i<=n; ++i) l[i]=hou[a[i]]+1,hou[a[i]]=i;
    for (int i=1; i<=n; ++i) l[i]=max(l[i-1],l[i]);
    int head=1,tail=0;
    for (int i=1; i<=n; ++i)
	{
        int k=i-1;
        while (head<tail && wei[head+1]<l[i])
		{
            t.erase(t.find(sum[head]));
            ++head;
        }
        while (head<=tail && b[i]>data[tail])
		{
            t.erase(t.find(sum[tail]));
            k=wei[tail];
            --tail;
        }
        data[++tail]=b[i];
        wei[tail]=k;
        if (head!=tail)
		{
            sum[tail]=f[wei[tail]]+data[tail];
            t.insert(sum[tail]);
            t.erase(t.find(sum[head]));
        }
        sum[head]=f[l[i]-1]+data[head];
        t.insert(sum[head]);
        f[i]=*t.begin();
    }
    printf("%d\n",f[n]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/89764175