CodeForces

题目

传送门

解法

(dp_i) 为推倒前 (i) 张骨牌的最小代价。考虑第 (i) 张骨牌向左推或不推(向右推实际上等价于后面的骨牌不推)。再设 (l_i) 为第 (i) 张骨牌向左推能推倒的最远骨牌,(r_i) 同理。

对于向左推,我们发现这相当于将 ([l_i,i]) 中的骨牌都推倒了,所以转移直接从 (l_i-1) 继承:(dp_i=dp_{l_i-1}+w_i)

对于不推,前面就必须有骨牌向右推倒,转移有:

[dp_i=min_{}{f_{j-1}+w_j | j<ile r_j} ]

但这是 (mathcal O(m^2)) 的,用线段树优化也有一个 (log),过不去。

可以发现,若骨牌 (i) 可以推倒骨牌 (j)(不妨设 (i<j)),那么向右推倒骨牌 (i) 延伸的长度一定大于 (j) 延伸的长度。

这和优化有什么关系呢?再清楚一点:假设能推倒 (i) 的骨牌集合为 (S_i),而 (i) 能推倒 (j),这就意味着 (S_isubseteq S_j)。这说明合法推倒集合是可以继承的!我们考虑用单调栈。

首先根据这条法则,我们可以计算出 (l_i,r_i)

对于不推的转移,这实际是一个取 (S_i) 中点 (j)(min_{}{f_{j-1}+w_j}) 的过程,用单调栈维护离 (i) 最近的能向右推倒它的点 (k),那么 (S_i) 就是 (S_k) 加上 (k) 这个单独元素,若令 (pre_i)(S_i) 中点 (j)(min_{}{f_{j-1}+w_j}),实际上就是 (dp_i=pre_{k})

所以实际上并不需要维护 (r_i)

代码

#include <stack> 
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;

#define print(x,y) write(x),putchar(y) 

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}

typedef long long ll;
typedef pair <int,ll> pii;

const int maxn=250005,maxl=1e7+2;

int n,m,l[maxl];
ll f[maxl],pre[maxl];
stack <int> s;
vector <pii> dom[maxn],d;

int main() {
	n=read(9),m=read(9);
	for(int i=1;i<=n;++i) {
		int k=read(9);
		for(int j=1;j<=k;++j)
			dom[i].push_back(make_pair(read(9),0));
		for(int j=1;j<=k;++j)
			dom[i][j-1].second=read(9);
	}
	d.push_back(make_pair(1e8,0));
	for(int q=read(9);q;--q) {
		int id=read(9),mul=read(9);
		for(int i=0;i<dom[id].size();++i)
			d.push_back(make_pair(dom[id][i].first,dom[id][i].second*mul));
	}
	for(int i=m;i;--i) {
		while(!s.empty() and i<=s.top()-d[s.top()].first)
			l[s.top()]=i+1,s.pop();
		s.push(i);
	}
	while(!s.empty()) s.pop();
	for(int i=1;i<=m;++i)
		if(!l[i]) l[i]=1;
	pre[0]=1e18,s.push(0);
	for(int i=1;i<=m;++i) {
		while(!s.empty() and d[s.top()].first+s.top()<=i)
			s.pop();
		f[i]=min(f[l[i]-1]+d[i].second,pre[s.top()]);
		pre[i]=min(pre[s.top()],f[i-1]+d[i].second);
		s.push(i);
	}
	print(f[m],'
');
	return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/15017681.html