可并堆
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
/**************************************************************
Problem: 4003
User: Tunix
Language: C++
Result: Accepted
Time:4380 ms
Memory:42216 kb
****************************************************************/ //Huce #5 B
#include<queue>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rep(i,n) for(int i=0;i<n;++i)
#define F(i,j,n) for(int i=j;i<=n;++i)
#define D(i,j,n) for(int i=j;i>=n;--i)
#define pb push_back
using namespace std;
typedef long long LL;
LL getint(){
LL v=,sign=; char ch=getchar();
while(ch<''||ch>'') {if (ch=='-') sign=-; ch=getchar();}
while(ch>=''&&ch<='') {v=v*+ch-''; ch=getchar();}
return v*sign;
}
const int N=;
const LL INF=1e17;
/*******************tamplate********************/
int head[N],to[N],next[N],cnt;
void add(int x,int y){
to[++cnt]=y; next[cnt]=head[x]; head[x]=cnt;
}
int fa[N],a[N],n,m,c[N],die[N];
LL h[N],v[N];
struct node{
LL v,mul,add; int l,r,dis;
}t[N];
#define L t[x].l
#define R t[x].r
void update(int x,LL mul,LL add){
if (!x) return;
if (mul== && add==) return;
t[x].v=t[x].v*mul+add;
t[x].add=t[x].add*mul+add;
t[x].mul*=mul;
}
void Push_down(int x){
update(L,t[x].mul,t[x].add);
update(R,t[x].mul,t[x].add);
t[x].mul=; t[x].add=;
}
int merge(int x,int y){
if (!x||!y) return x+y;
if (t[x].v>t[y].v) swap(x,y);
Push_down(x);
R=merge(R,y);
if (t[L].dis<t[R].dis) swap(L,R);
t[x].dis=t[R].dis+;
return x;
}
int dep[N],rt[N],death[N];
void dfs(int x){
for(int i=head[x];i;i=next[i]){
dep[to[i]]=dep[x]+; dfs(to[i]);
rt[x]=merge(rt[x],rt[to[i]]);
}
while(rt[x] && t[rt[x]].v<h[x]){
death[x]++;
Push_down(rt[x]);
die[rt[x]]=x;
rt[x]=merge(t[rt[x]].l,t[rt[x]].r);
}
if (a[x]) update(rt[x],v[x],);
else update(rt[x],,v[x]);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("B.in","r",stdin);
#endif
n=getint(); m=getint(); t[].v=INF;
F(i,,n) h[i]=getint();
F(i,,n){
fa[i]=getint(); a[i]=getint(); v[i]=getint();
add(fa[i],i);
}
F(i,,m){
t[i].v=getint();t[i].add=;t[i].mul=; c[i]=getint();
rt[c[i]]=merge(rt[c[i]],i);
}
dep[]=; dfs();
F(i,,n) printf("%d\n",death[i]);
F(i,,m) printf("%d\n",dep[c[i]]-dep[die[i]]);
return ;
}
4003: [JLOI2015]城池攻占
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 39 Solved: 16
[Submit][Status][Discuss]
Description
小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池。
ai =1,攻占城池 i 以后,战斗力会乘以
vi。注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。
Input
第 1 行包含两个正整数 n;m,表示城池的数量和骑士的数量。
Output
输出 n + m 行,每行包含一个非负整数。其中前 n 行分别表示在城池 1 到 n 牺牲的骑士
Sample Input
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
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。