[JLOI2015]城池攻占

嘟嘟嘟

 

暴力不难想:一种是以每一个骑士为主,一步步向上爬直到死了;一种是以每一个城池为主,统计哪些骑士在这座城池中死了,然后剩下的骑士再转移到他的父亲节点。

考虑优化:因为城池构成了一个树形结构,相对于骑士来说结构比较固定。因此我们想办法优化第二个暴力:每一个节点建一个小根堆,一直弹出堆顶直到堆顶骑士的战斗力>=防御值,然后把剩下的骑士都转移到他的父亲节点取。所以说,父亲节点的骑士是由他的子节点的骑士合并而来的。那么这个堆还要支持合并,左偏树是一个不错的选择。

左偏树是可以打标记的,因此修改就和线段树有点像,打一个lazy标记,然后处理到这个节点的时候pushdown就行了。

最后说一下统计答案:每一个城池牺牲多少人好办,开一计数数组就解决了。关键是统计每一个骑士攻占了多少城池。原来我的做法是每攻占了一个城池,就给左偏树的所有骑士++,实际上有一个更好的办法:维护树中每一个节点的深度,则一个骑士攻占的城池数量就是第一个攻占的城池的深度减去死的城池的深度。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cctype>
  8 #include<vector>
  9 #include<stack>
 10 #include<queue>
 11 using namespace std;
 12 #define enter puts("") 
 13 #define space putchar(' ')
 14 #define Mem(a, x) memset(a, x, sizeof(a))
 15 #define rg register
 16 typedef long long ll;
 17 typedef double db;
 18 const int INF = 0x3f3f3f3f;
 19 const db eps = 1e-8;
 20 const int maxn = 3e5 + 5;
 21 inline ll read()
 22 {
 23     ll ans = 0;
 24     char ch = getchar(), last = ' ';
 25     while(!isdigit(ch)) {last = ch; ch = getchar();}
 26     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
 27     if(last == '-') ans = -ans;
 28     return ans;
 29 }
 30 inline void write(ll x)
 31 {
 32     if(x < 0) x = -x, putchar('-');
 33     if(x >= 10) write(x / 10);
 34     putchar(x % 10 + '0');
 35 }
 36 
 37 int n, m;
 38 ll h[maxn], v[maxn]; 
 39 bool a[maxn];
 40 ll s[maxn];
 41 int c[maxn];
 42 int ans1[maxn], ans2[maxn];
 43 struct Edge
 44 {
 45     int nxt, to;
 46 }e[maxn];
 47 int head[maxn], ecnt = 0;
 48 void addEdge(int x, int y)
 49 {
 50     e[++ecnt] = (Edge){head[x], y};
 51     head[x] = ecnt;
 52 }
 53 
 54 int root[maxn], ls[maxn], rs[maxn], dis[maxn], dep[maxn];
 55 ll lzy_mul[maxn], lzy_add[maxn];
 56 void Sign(int now, ll mul, ll add)
 57 {
 58     if(!now) return;
 59     s[now] *= mul; s[now] += add;
 60     lzy_mul[now] *= mul; lzy_add[now] *= mul; lzy_add[now] += add;
 61 }
 62 void pushdown(int now)
 63 {
 64     Sign(ls[now], lzy_mul[now], lzy_add[now]);
 65     Sign(rs[now], lzy_mul[now], lzy_add[now]);
 66     lzy_mul[now] = 1; lzy_add[now] = 0;
 67 }
 68 int merge(int x, int y)
 69 {
 70     if(!x || !y) return x | y;
 71     pushdown(x); pushdown(y);
 72     if(s[x] > s[y]) swap(x, y);
 73     rs[x] = merge(rs[x], y);
 74     if(dis[ls[x]] < dis[rs[x]]) swap(ls[x], rs[x]);
 75     dis[x] = dis[rs[x]] + 1;
 76     return x;
 77 }
 78 int Del(int now)
 79 {
 80     return merge(ls[now], rs[now]);
 81 }
 82 
 83 void dfs(int now)
 84 {
 85     for(int i = head[now]; i; i = e[i].nxt)
 86     {
 87         dep[e[i].to] = dep[now] + 1;
 88         dfs(e[i].to);
 89         root[now] = merge(root[now], root[e[i].to]);
 90     }
 91     while(root[now] && s[root[now]] < h[now])
 92     {
 93         pushdown(root[now]);
 94         ans1[now]++; 
 95         ans2[root[now]] = dep[c[root[now]]] - dep[now];
 96         root[now] = Del(root[now]);
 97     }
 98     if(a[now]) Sign(root[now], v[now], 0);
 99     else Sign(root[now], 1, v[now]);
100 }
101 
102 int main()
103 {
104     n = read(); m = read();
105     for(int i = 1; i <= n; ++i) h[i] = read();
106     for(int i = 2; i <= n; ++i)
107     {
108         int x = read(); a[i] = (bool)read(); v[i] = read();
109         addEdge(x, i);
110     }
111     for(int i = 1; i <= m; ++i)
112     {
113         s[i] = read(); c[i] = read();
114         lzy_mul[i] = 1;
115         root[c[i]] = merge(root[c[i]], i);
116     }
117     dep[1] = 1; dfs(1);
118     while(root[1])
119     {
120         pushdown(root[1]);
121         ans2[root[1]] = dep[c[root[1]]];
122         root[1] = Del(root[1]);
123     }
124     for(int i = 1; i <= n; ++i) write(ans1[i]), enter;
125     for(int i = 1; i <= m; ++i) write(ans2[i]), enter;
126     return 0;
127 }
View Code
原文地址:https://www.cnblogs.com/mrclr/p/9801985.html