【BZOJ】【4003】【JLOI2015】城池攻占

可并堆


  QAQ改了一下午……最终弃疗求助zyf……居然被秒了QAQ真是弱到不行(zyf太神了Orz)

  还是先考虑部分分的做法:

  1.$n,m\leq 3000$:可以暴力模拟每个骑士的攻打过程,也可以利用拓扑序,将当前城池的后代的攻打情况统计完后,再统计有哪些其实打到了当前城池,over了几个,又有几个继续前进了……时间复杂度应该是O(n*m)的吧。

  2.一条链的情况 >_>没想出来

  3.所有的骑士武力值都不变的情况:可以用倍增搞出每个骑士如果想打到第$2^k$个祖先处最小需要多大的武力值(其实就是这一段的城池的hp的最大值),然后努力往上爬树吧,从大到小枚举2的幂,就像倍增LCA那样……时间复杂度应该是O(m*logn)的吧……

 

  然后我从第三部分的思路想到了一个“正解”出来(想看正解请跳过这段,只是记录下自己的sb之处……):骑士的武力值变起来太麻烦了,我们可以看成是城池的hp在变,如果某个后代节点的武力改变是×2,那么就相当于从这个后代过来的骑士,要想攻占当前这个城池,武力值至少得是h[i]/2,那么就可以倍增什么的……

  但是致命的错误在于:每次向下走的时候,这个-v[i]或者除以v[i]的操作应该是对所有的祖先都有效的,而不是仅仅对父亲……所以我就跪了QAQ自己还打了1个多小时……sb啊……

 

  正解其实可以从做法1扩展过来:我们每次是将攻占了当前城池的每个骑士都单独改变下他们每一个人的武力值,再送到父亲城池,然后再在父亲城池考虑哪些骑士会挂掉。最后一步可以看做是不断删除最小的武力值(死亡),第二步是合并多组武力值,第一步……可以在数据结构上打标记来实现。

  那么很明显了……splay启发式合并或者更简单的,写一个可并堆就可以搞定了= =

 

我改了一下午的地方……在update的时候,mul和add两个参数应该是long long!!!我只是开数组的时候开了long long,调用这两个标记的值的时候就忘了QAQ

 1 /**************************************************************
 2     Problem: 4003
 3     User: Tunix
 4     Language: C++
 5     Result: Accepted
 6     Time:4380 ms
 7     Memory:42216 kb
 8 ****************************************************************/
 9  
10 //Huce #5 B
11 #include<queue>
12 #include<vector>
13 #include<cstdio>
14 #include<cstdlib>
15 #include<cstring>
16 #include<iostream>
17 #include<algorithm>
18 #define rep(i,n) for(int i=0;i<n;++i)
19 #define F(i,j,n) for(int i=j;i<=n;++i)
20 #define D(i,j,n) for(int i=j;i>=n;--i)
21 #define pb push_back
22 using namespace std;
23 typedef long long LL;
24 LL getint(){
25     LL v=0,sign=1; char ch=getchar();
26     while(ch<'0'||ch>'9') {if (ch=='-') sign=-1; ch=getchar();}
27     while(ch>='0'&&ch<='9') {v=v*10+ch-'0'; ch=getchar();}
28     return v*sign;
29 }
30 const int N=300010;
31 const LL INF=1e17;
32 /*******************tamplate********************/
33 int head[N],to[N],next[N],cnt;
34 void add(int x,int y){
35     to[++cnt]=y; next[cnt]=head[x]; head[x]=cnt;
36 }
37 int fa[N],a[N],n,m,c[N],die[N];
38 LL h[N],v[N];
39 struct node{
40     LL v,mul,add; int l,r,dis;
41 }t[N];
42 #define L t[x].l
43 #define R t[x].r
44 void update(int x,LL mul,LL add){
45     if (!x) return;
46     if (mul==1 && add==0) return;
47     t[x].v=t[x].v*mul+add;
48     t[x].add=t[x].add*mul+add;
49     t[x].mul*=mul;
50 }
51 void Push_down(int x){
52     update(L,t[x].mul,t[x].add);
53     update(R,t[x].mul,t[x].add);
54     t[x].mul=1; t[x].add=0;
55 }
56 int merge(int x,int y){
57     if (!x||!y) return x+y;
58     if (t[x].v>t[y].v) swap(x,y);
59     Push_down(x);
60     R=merge(R,y);
61     if (t[L].dis<t[R].dis) swap(L,R);
62     t[x].dis=t[R].dis+1;
63     return x;
64 }
65 int dep[N],rt[N],death[N];
66 void dfs(int x){
67     for(int i=head[x];i;i=next[i]){
68         dep[to[i]]=dep[x]+1; dfs(to[i]);
69         rt[x]=merge(rt[x],rt[to[i]]);
70     }
71     while(rt[x] && t[rt[x]].v<h[x]){
72         death[x]++;
73         Push_down(rt[x]);
74         die[rt[x]]=x;
75         rt[x]=merge(t[rt[x]].l,t[rt[x]].r);
76     }
77     if (a[x]) update(rt[x],v[x],0);
78     else update(rt[x],1,v[x]);
79 }
80 int main(){
81 #ifndef ONLINE_JUDGE
82     freopen("B.in","r",stdin);
83 #endif
84     n=getint(); m=getint(); t[0].v=INF;
85     F(i,1,n) h[i]=getint();
86     F(i,2,n){
87         fa[i]=getint(); a[i]=getint(); v[i]=getint();
88         add(fa[i],i);
89     }
90     F(i,1,m){
91         t[i].v=getint();t[i].add=0;t[i].mul=1; c[i]=getint();
92         rt[c[i]]=merge(rt[c[i]],i);
93     }
94     dep[1]=1; dfs(1);
95     F(i,1,n) printf("%d\n",death[i]);
96     F(i,1,m) printf("%d\n",dep[c[i]]-dep[die[i]]);
97     return 0;
98 }
View Code

4003: [JLOI2015]城池攻占

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 39  Solved: 16
[Submit][Status][Discuss]

Description

小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池。

这 n 个城池用 1 到 n 的整数表示。除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖,
其中 fi <i。也就是说,所有城池构成了一棵有根树。这 m 个骑士用 1 到 m 的整数表示,其
中第 i 个骑士的初始战斗力为 si,第一个攻击的城池为 ci。
每个城池有一个防御值 hi,如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可
以占领这座城池;否则占领失败,骑士将在这座城池牺牲。占领一个城池以后,骑士的战斗力
将发生变化,然后继续攻击管辖这座城池的城池,直到占领 1 号城池,或牺牲为止。
除 1 号城池外,每个城池 i 会给出一个战斗力变化参数 ai;vi。若 ai =0,攻占城池 i 以后骑士战斗力会增加 vi;若 ai =1,攻占城池 i 以后,战斗力会乘以 vi。注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。
现在的问题是,对于每个城池,输出有多少个骑士在这里牺牲;对于每个骑士,输出他攻占的城池数量。

Input

第 1 行包含两个正整数 n;m,表示城池的数量和骑士的数量。

第 2 行包含 n 个整数,其中第 i 个数为 hi,表示城池 i 的防御值。
第 3 到 n +1 行,每行包含三个整数。其中第 i +1 行的三个数为 fi;ai;vi,分别表示管辖
这座城池的城池编号和两个战斗力变化参数。
第 n +2 到 n + m +1 行,每行包含两个整数。其中第 n + i 行的两个数为 si;ci,分别表
示初始战斗力和第一个攻击的城池。

Output

 输出 n + m 行,每行包含一个非负整数。其中前 n 行分别表示在城池 1 到 n 牺牲的骑士

数量,后 m 行分别表示骑士 1 到 m 攻占的城池数量。

Sample Input

5 5
50 20 10 10 30
1 1 2
2 0 5
2 0 -10
1 0 10
20 2
10 3
40 4
20 4
35 5

Sample Output

2
2
0
0
0
1
1
3
1
1

HINT

 对于 100% 的数据,1 <= n;m <= 300000; 1 <= fi<i; 1 <= ci <= n; -10^18 <= hi,vi,si <= 10^18;ai等于1或者2;当 ai =1 时,vi > 0;保证任何时候骑士战斗力值的绝对值不超过 10^18。


Source

[Submit][Status][Discuss]
posted @ 2015-04-21 18:20  Tunix  阅读(563)  评论(0编辑  收藏  举报