洛谷P3261 [JLOI2015]城池攻占

 

思路分析:由于这道题的数据范围是n,m<=3e5,所以我们直接输入一个模拟一个是会超时的,但是我们可以在输入所有的士兵之后把同在一个节点的士兵一起处理,我们可以考虑建一个堆,从深度最大的节点开始,维护一个节点内的士兵的最小战斗力值,如果战斗力最小的士兵都能存活下来,那么在堆中的其他士兵一定可以活下来,之后我们在处理完一个节点后要把这个节点中所有存活的士兵都移到它的父亲节点之中,但由于父亲节点之中可能也存在士兵(即从该节点开始),那么我们就需要把这两个堆里面的元素合并在一起,于是我们想到用可并堆,即左偏树,来维护和解决这个问题,我们记录每个士兵开始和阵亡(或活到最后)的位置,之后第二部分的答案就是开始的深度减去阵亡的深度。

另外在写左偏树的时候,我们合并时有一步是当前节点的距离是右儿子的距离加一(符合左偏树的性质),但在写的时候蒟蒻发现好像让节点的距离为左儿子的距离加一好像也可以过,后来问gjk大佬,说这样也是可以的,只是时间复杂度好像从log级别退化为了根号级别的。

 代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std; 
 5 typedef long long ll;
 6 const int N=3e5+10;
 7 ll n,m;
 8 ll fa[N],c[N],a[N],rt[N];
 9 ll h[N],v[N],s[N];
10 ll ls[N],rs[N],Dep[N];
11 ll dep[N],die[N],ans[N];
12 ll add[N],cheng[N];
13 void push_down(ll x){ //把当前节点的信息传给子节点,相当于lazy标记 
14     if(add[x]==0&&cheng[x]==1) return; 
15     if(ls[x]){
16         cheng[ls[x]]*=cheng[x];
17         add[ls[x]]*=cheng[x];
18         add[ls[x]]+=add[x];
19         s[ls[x]]*=cheng[x];
20         s[ls[x]]+=add[x];
21     }
22     if(rs[x]){
23         cheng[rs[x]]*=cheng[x];
24         add[rs[x]]*=cheng[x];
25         add[rs[x]]+=add[x];
26         s[rs[x]]*=cheng[x];
27         s[rs[x]]+=add[x];
28     }
29     add[x]=0,cheng[x]=1;
30 }
31 ll merge(ll x,ll y){  //左偏树的合并操作 
32     if(!x||!y) return x+y;
33     push_down(x),push_down(y);
34     if(s[x]>s[y]) swap(x,y);
35     rs[x]=merge(rs[x],y);
36     if(dep[ls[x]]<dep[rs[x]])
37         swap(ls[x],rs[x]);
38     dep[x]=dep[rs[x]]+1;
39     return x;
40 
41 }
42 
43 int main(){
44     //freopen("a.txt","r",stdin);
45     scanf("%lld%lld",&n,&m);
46     for(int i=1;i<=n;++i){
47         scanf("%lld",&h[i]);
48         rt[i]=-1;  //rt记录的是当前节点的左偏树的根节点 
49     }
50     Dep[1]=1;dep[0]=-1;//Dep是深度,dep是树高 
51     for(int i=2;i<=n;++i){
52         scanf("%lld%lld%lld",&fa[i],&a[i],&v[i]);
53         Dep[i]=Dep[fa[i]]+1;//省去dfs 
54     }
55     for(int i=1;i<=m;++i){
56         scanf("%lld%lld",&s[i],&c[i]);
57         cheng[i]=1;
58         if(rt[c[i]]==-1) rt[c[i]]=i;
59         else rt[c[i]]=merge(rt[c[i]],i);
60     }
61     for(int i=n;i>=1;i--){
62         while(rt[i]!=-1){//最小士兵阵亡 
63             if(s[rt[i]]<h[i]){
64                 die[rt[i]]=i;
65                 push_down(rt[i]);
66                 if(!ls[rt[i]]) rt[i]=-1;
67                 else rt[i]=merge(ls[rt[i]],rs[rt[i]]); 
68             }
69             else break;
70         }
71         if(i==1) break;
72         if(rt[i]==-1) continue;
73         if(a[i]) cheng[rt[i]]*=v[i],add[rt[i]]*=v[i],s[rt[i]]*=v[i];
74         else add[rt[i]]+=v[i],s[rt[i]]+=v[i];
75         push_down(rt[i]);
76         if(rt[fa[i]]==-1) rt[fa[i]]=rt[i];  //存活士兵并到父亲上 
77         else rt[fa[i]]=merge(rt[fa[i]],rt[i]);
78     }
79     for(int i=1;i<=m;++i) //统计答案 
80         ans[die[i]]++;
81     for(int i=1;i<=n;++i)
82         printf("%lld
",ans[i]);
83     for(int i=1;i<=m;++i)
84         printf("%lld
",Dep[c[i]]-Dep[die[i]]);
85     return 0;
86 }
View Code
原文地址:https://www.cnblogs.com/li-jia-hao/p/12930097.html