PAT-GPLT L3-017 森森快递(贪心 + 线段树)
链接:
https://www.patest.cn/contests/gplt/L3-017
题意:
给出直线上的N个顶点,(N-1)条边的限制值(每对相邻的顶点之间都有一条边),以及Q个区间(给出起始顶点编号以及终止顶点编号)。
每个区间都可以为该区间的所有边加上一个附加值,所有区间在某条边上所累加的附加值不能超过这条边的限制值。
问:所有区间的附加值总和最大是多少?
分析:
先按终点编号(将原来起点与终点编号较大的作为终点编号)从小到大排序,然后贪心选择:
顺序考虑每一个区间,用线段树快速找到该区间的最小值,然后更新该区间。
最后所有区间能找到的最小值总和就是答案。
至于为什么要按终点编号从小到大排序,而不是按起点编号从小到大排序,或按区间长度从小到大排序,看一下下面的测试数据就明白了。
不过这道题目说每个限制值是不超过2的31次方的非负整数,为什么偏要 long long 才能过呢?
代码:
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 typedef long long int LLI; 6 const LLI INF = 0x3f3f3f3f3f3f3f3f; 7 const int UP = 1e5 + 5; 8 9 struct SEGMENT_TREE_NODE { 10 LLI v, m; //值,标记 11 } st[UP<<2]; 12 13 struct REGION { 14 int L, R; 15 bool operator < (const REGION& that) const { 16 return R < that.R; 17 } 18 } reg[UP]; 19 20 void build(int root, int L, int R){ 21 st[root].m = 0; 22 if(L + 1 == R){ 23 scanf("%lld", &st[root].v); 24 return; 25 } 26 int M = L + (R - L) / 2; 27 build(root*2+1, L, M); 28 build(root*2+2, M, R); 29 st[root].v = min(st[root*2+1].v, st[root*2+2].v); 30 } 31 32 void push_down(int root){ 33 if(!st[root].m) return; 34 st[root*2+1].v += st[root].m; 35 st[root*2+2].v += st[root].m; 36 st[root*2+1].m += st[root].m; 37 st[root*2+2].m += st[root].m; 38 st[root].m = 0; 39 } 40 41 LLI query(int root, int L, int R, int AL, int AR){ 42 if(AR <= L || AL >= R) return INF; 43 if(AL <= L && R <= AR) return st[root].v; 44 push_down(root); 45 int M = L + (R - L) / 2; 46 return min(query(root*2+1, L, M, AL, AR), query(root*2+2, M, R, AL, AR)); 47 } 48 49 void update(int root, int L, int R, int AL, int AR, LLI v){ 50 if(AR <= L || AL >= R) return; 51 if(AL <= L && R <= AR){ 52 st[root].v += v; 53 st[root].m += v; 54 return; 55 } 56 push_down(root); 57 int M = L + (R - L) / 2; 58 update(root*2+1, L, M, AL, AR, v); 59 update(root*2+2, M, R, AL, AR, v); 60 st[root].v = min(st[root*2+1].v, st[root*2+2].v); 61 } 62 63 int main(){ 64 int n, q; 65 scanf("%d%d", &n, &q); 66 build(0, 0, --n); 67 68 for(int i = 0; i < q; i++){ 69 scanf("%d%d", ®[i].L, ®[i].R); 70 if(reg[i].L > reg[i].R) swap(reg[i].L, reg[i].R); 71 } 72 sort(reg, reg + q); 73 74 LLI ans = 0; 75 for(int i = 0; i < q; i++){ 76 LLI v = query(0, 0, n, reg[i].L, reg[i].R); 77 ans += v; 78 if(v) update(0, 0, n, reg[i].L, reg[i].R, -v); 79 } 80 printf("%lld\n", ans); 81 return 0; 82 }
测试数据:
input1:
5 3
5 3 3 1
4 1
1 3
3 4
output1:
4
input2:
5 3
3 9 9 1
3 1
2 0
2 4
output2:
10