题目
解法
设 (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;
}