链接
题意
在n个村庄之间存在n-1段路,令某段路开放一天需要交纳wi的费用,但是每段路只能开放一次,一旦关闭将不再开放。现在给你接下来m天内的计划,在第i天,需要对村庄ai到村庄bi的道路进行开放。在满足m天内花费最小的情况下,求出每天的花销。
思路
我们需要求出每段路的开放时间区间[s, t],因为某段路可能在某天不用,但是在这天前后使用了,那么这天同样要收费。
事实上是一个区间更新的问题,我们对道路建立线段树,在第i天给出的信息是道路的区间,我们将这个区间内道路的结束时间全部更新为i,这样我们就能对每段道路得到它时间区间的右端点t。左端点s“先到先得”,在这段道路第一次更新的时候确定其开放的起始时间。
现在每段道路的开放时间区间[s, t]有了,可以将其看做“询问”,对时间建立线段树,每段道路令[s, t]内的每天增加wi的费用,这就又是一个区间更新的问题。
但是这次的更新比较简单,只是单纯让区间内的所有值增上某个值而已,使用差分数组就可以在O(n)内解决了。
PS:这道题目感觉出得还挺好的,如果对线段树的lazy标记理解不深刻就可能容易做不出来。因为在成段更新区间的时候,对开始时间和结束时间的处理是完全不同的。
代码
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long lint;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 200020;
int Begin[maxn << 2], End[maxn << 2];
void build(int l, int r, int rt)
{
Begin[rt] = End[rt] = 0;
if(l == r) return ;
int m = (l + r) >> 1;
build(lson), build(rson);
}
inline void _push_down(int rt)
{
if(!Begin[rt<<1]) Begin[rt<<1] = Begin[rt];
if(!Begin[rt<<1|1]) Begin[rt<<1|1] = Begin[rt];
if(!End[rt]) return ;
End[rt<<1] = End[rt<<1|1] = End[rt];
End[rt] = 0;
}
void update(int L, int R, int k, int l, int r, int rt)
{
if(L <= l && r <= R)
{
if(!Begin[rt]) Begin[rt] = k;
End[rt] = k;
return ;
}
_push_down(rt);
int m = (l + r) >> 1;
if(m >= L) update(L, R, k, lson);
if(m < R) update(L, R, k, rson);
}
int bg[maxn], ed[maxn];
void push_all(int l, int r, int rt)
{
if(l == r)
{
bg[l] = Begin[rt], ed[l] = End[rt];
return ;
}
_push_down(rt);
int m = (l + r) >> 1;
push_all(lson), push_all(rson);
}
void test(int l, int r)
{
for(int i = l; i <= r; i++)
{
printf("# bg = %d, ed = %d\n", bg[i], ed[i]);
}
}
int w[maxn];
lint a[maxn], d[maxn];
int main()
{
//freopen("5.txt", "r", stdin);
int n, m;
while(cin >> n >> m)
{
build(1, n-1, 1);
for(int i = 1; i < n; i++)
scanf("%d", &w[i]);
for(int i = 1, p, q; i <= m; i++)
{
scanf("%d%d", &p, &q);
if(p > q) swap(p, q);
update(p, q-1, i, 1, n-1, 1);
}
push_all(1, n-1, 1);
//test(1, n-1);
memset(d, 0, sizeof(lint)*(m+10));
for(int i = 1; i < n; i++) if(bg[i])
{
d[bg[i]] += w[i];
d[ed[i]+1] -= w[i];
}
a[0] = 0;
for(int i = 1; i <= m; i++)
{
a[i] = a[i-1] + d[i];
printf("%I64d\n", a[i]);
}
}
return 0;
}