题目
题目背景
“当血月出现之时,标杆之力 的拥有者靠近它时会睁开 孤独之眼,”
D
D
(
X
Y
X
)
\sf DD(XYX)
DD(XYX) 的额头上,血色的第三只眼慢慢张开,“「无限月独」!”
大地被照得雪亮。这光能穿过影子。所有人都无一例外,进入了像月亮一般孤独的世界。
“「神丶树界降临」!”
题目描述
面前是一棵巨大的神树。所谓树,即
(
n
−
1
)
(n{\rm-}1)
(n−1) 条边、
n
n
n 个点的连通图。这树可以吸取「卷坷垃」。对于
x
x
x 号节点,若其吸收了
a
a
a 份「卷坷垃」,则到它距离为
d
(
d
⩾
1
)
d\;(d\geqslant 1)
d(d⩾1) 的节点也会获得
⌊
a
2
d
⌋
\lfloor{a\over 2^d}\rfloor
⌊2da⌋ 的「卷坷垃」增幅。——当然,
x
x
x 节点自身的「卷坷垃」增幅是
a
a
a 。
现在,你需要动态计算神树的「卷坷垃」吸收情况,因为一个没有「卷坷垃」的孤独世界就要来临了!具体而言,有下列两种情况:
- x x x 号节点吸收了 a a a 份「卷坷垃」。
- 若 y y y 到 1 1 1 号节点的路径上经过 x x x,则称 x x x 是 y y y 的先辈。给出 x x x,求出所有以 x x x 为先辈的 y y y 中,有多少个满足,其「卷坷垃」量至少是 c y c_y cy 。
最初每个节点的「卷坷垃」量都是 0 0 0 。
数据范围与约定
max
(
n
,
q
,
a
)
⩽
1
0
5
\max(n,q,a)\leqslant 10^5
max(n,q,a)⩽105,但是
c
i
∈
[
1
,
1
0
9
]
c_i\in[1,10^9]
ci∈[1,109] 。
思路
我以为我学懂了 时间轴,结果还是不会做……
显然只需要求出每个点最早使得「卷坷垃」量超过 c c c 的时刻。那么可以 整体二分。因为这时候 “询问” 其实是与修改没有时间顺序要求的。
整体二分,建虚树之后,每个点记录该点上的等价 2 k 2^k 2k 数量。因为 ∑ ⌊ a 2 d ⌋ = ∑ ⌊ a ÷ 2 2 d − 1 ⌋ ≠ ( ∑ ⌊ a 2 d − 1 ⌋ ) ÷ 2 \sum\lfloor{a\over 2^d}\rfloor=\sum\lfloor\frac{a\div 2}{2^{d-1}}\rfloor\ne\big(\sum\lfloor{a\over 2^{d-1}}\rfloor\big)\div 2 ∑⌊2da⌋=∑⌊2d−1a÷2⌋=(∑⌊2d−1a⌋)÷2 。本质上是因为,每个二进制位的效果不同,相加时不能进位。所以只能记录含有 2 k 2^k 2k 的 a a a 的数量。将这个 t a g \rm tag tag 推给儿子,就是原本含有的 2 k 2^k 2k 变为 2 k − 1 2^{k-1} 2k−1 。
类似换根 d p \tt dp dp 的方式,做两次 d f s \tt dfs dfs 就可以得到若干修改对所有点的贡献。单次复杂度 O ( L log a ) \mathcal O(L\log a) O(Lloga),其中 L L L 是点数,所以总复杂度 O ( n log n log a ) \mathcal O(n\log n\log a) O(nlognloga) 。
代码
#include <cstdio> // JZM yydJUNK!!!
#include <iostream> // XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // (the STRONG long for LONELINESS)
#include <cctype> // ZXY yydSISTER!!!
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define drep0(i,a,b) for(int i=(a); i!=(b); --i)
typedef long long llong;
const int BUFFER_LENGTH = 1<<22;
char in_buf[BUFFER_LENGTH];
inline char getChar(){
static char *S = in_buf, *T = S;
if(S == T) S = in_buf, T = S +
fread(S,1,BUFFER_LENGTH,stdin);
return *(S ++);
}
inline int readint(){
int a = 0, c = getChar(), f = 1;
for(; !isdigit(c); c=getChar())
if(c == '-') f = -f;
for(; isdigit(c); c=getChar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(int x){
if(x > 9) writeint(x/10);
putchar(int(x%10)^48);
}
const int MAXN = 100005;
# define _go(i,x) for(int i=head[x]; ~i; i=e[i].nxt)
namespace Graph{
struct Edge{ int to, nxt; };
Edge e[MAXN<<1]; int head[MAXN], cntEdge;
void addEdge(int a, int b){
e[cntEdge].to = b, e[cntEdge].nxt = head[a], head[a] = cntEdge ++;
e[cntEdge].to = a, e[cntEdge].nxt = head[b], head[b] = cntEdge ++;
}
void input(const int &n){
memset(head+1,-1,n<<2), cntEdge = 0;
rep0(i,1,n) addEdge(readint(),readint());
}
const int LOGN = 18;
int dep[MAXN], fa[MAXN][LOGN];
int dfn[MAXN], dfsClock, ed[MAXN];
void scan(int x, int pre){
dfn[x] = ++ dfsClock;
for(int j=0; fa[x][j]; ++j)
fa[x][j+1] = fa[fa[x][j]][j];
_go(i,x) if((i^1) != pre){
dep[e[i].to] = dep[x]+1;
fa[e[i].to][0] = x, scan(e[i].to,i);
}
ed[x] = dfsClock;
}
int getLca(int a, int b){
if(dep[a] < dep[b]) swap(a,b);
drep(j,LOGN-1,0) // first jump
if((dep[a]-dep[b])>>j&1)
a = fa[a][j];
if(a == b) return a;
drep(j,LOGN-1,0)
if(fa[a][j] != fa[b][j])
a = fa[a][j], b = fa[b][j];
return fa[a][0];
}
inline int eval(int a, int b){
return dep[b]-dep[a];
}
}
namespace Vtree{
struct Edge{ int to, nxt, val; };
Edge e[MAXN<<1]; int head[MAXN], cntEdge;
void addEdge(int a, int b, int c){
e[cntEdge].to = b, e[cntEdge].nxt = head[a];
e[cntEdge].val = c, head[a] = cntEdge ++;
}
const int LOGA = 17;
int cnt[MAXN][LOGA];
bool cmp(const int &a, const int &b){
return Graph::dfn[a] < Graph::dfn[b];
}
int build(int a[], int len){
using namespace Graph;
cntEdge = 0; // init cntEdge!!!
std::sort(a+1,a+len+1,cmp);
len = int(unique(a+1,a+len+1)-a-1);
static int _sta[MAXN], _top;
_sta[_top = 1] = getLca(a[1],a[len]);
if(a[1] != _sta[1]){
_sta[_top = 2] = a[1];
memset(cnt[a[1]],0,LOGA<<2);
}
memset(cnt[_sta[1]],0,LOGA<<2); // init cnt
head[a[1]] = head[_sta[1]] = -1; // init head
for(int i=2,lca; i<=len; ++i){
lca = getLca(a[i-1],a[i]);
head[a[i]] = -1; // init head
memset(cnt[a[i]],0,LOGA<<2); // init cnt
for(; _top>1&&!cmp(_sta[_top-1],lca); --_top)
addEdge(_sta[_top-1],_sta[_top],
eval(_sta[_top-1],_sta[_top]));
if(lca != _sta[_top]){
head[lca] = -1, memset(cnt[lca],0,LOGA<<2);
addEdge(lca,_sta[_top],eval(lca,_sta[_top]));
_sta[_top] = lca; // higher
}
_sta[++ _top] = a[i];
}
while(-- _top) addEdge(_sta[_top],_sta[_top+1],
eval(_sta[_top],_sta[_top+1]));
return _sta[1]; // root of virtual tree
}
void addTag(int x, int v){
for(int i=0; v; ++i,v>>=1)
cnt[x][i] += (v&1);
}
int above[MAXN][LOGA];
void toTop(int x){
memset(above[x],0,LOGA<<2);
_go(i,x){
toTop(e[i].to); // add subtree
if(e[i].val < LOGA) rep0(j,e[i].val,LOGA)
cnt[x][j-e[i].val] += cnt[e[i].to][j];
}
}
void toAss(int x){
rep0(j,0,LOGA) above[x][j] += cnt[x][j];
_go(i,x){
const int &y = e[i].to, &w = e[i].val;
if((w<<1) < LOGA) rep0(j,w<<1,LOGA)
above[y][j-(w<<1)] -= cnt[y][j];
if(w < LOGA) rep0(j,w,LOGA)
above[y][j-w] += above[x][j];
toAss(y); // give to subtree
}
}
inline llong query(int x){
long long res = 0;
drep(i,LOGA-1,0) res = (res<<1)+above[x][i];
return res;
}
}
struct Command{ int x, a; };
Command cmd[MAXN]; ///< modification
int tim[MAXN], id[MAXN], c[MAXN];
/**
* @brief consider modification in [ @p l , @p r ],
* get the transforming time of node in id [ @p ql , @p qr ]
*/
void solve(int l, int r, int ql, int qr){
if(l > r || ql > qr) return ;
const int mid = (l+r)>>1;
static int _tmp[MAXN<<1];
rep(i,l,mid) _tmp[i-l+1] = cmd[i].x;
memcpy(_tmp+(mid-l+1)+1,id+ql,(qr-ql+1)<<2);
int rt = Vtree::build(_tmp,(mid-l+1)+(qr-ql+1));
rep(i,l,mid) Vtree::addTag(cmd[i].x,cmd[i].a);
Vtree::toTop(rt), Vtree::toAss(rt);
int pl = ql-1, pr = qr+1; ///< rearrange @a id
rep(i,ql,qr){
llong val = Vtree::query(id[i]);
if(val < c[id[i]]) // cdq yyds!!!
c[id[i]] -= int(val), _tmp[-- pr] = id[i];
else tim[id[i]] = mid, _tmp[++ pl] = id[i];
}
memcpy(id+ql,_tmp+ql,(pl-ql+1)<<2);
memcpy(id+pr,_tmp+pr,(qr-pr+1)<<2);
solve(l,mid-1,ql,pl), solve(mid+1,r,pr,qr);
}
namespace BIT{
int c[MAXN];
void modify(int id, const int &n){
for(int i=id; i<=n; i+=(i&-i)) ++ c[i];
}
inline int query(int id){
int res = 0;
for(int i=id; i; i&=(i-1)) res += c[i];
return res;
}
}
bool cmp(const int &x, const int &y){
return tim[x] < tim[y];
}
int ask[MAXN][2];
int main(){
int n = readint();
rep(i,1,n) c[i] = readint();
Graph::input(n), Graph::scan(1,-1);
int stamp = 0; ///< current time
int m = readint(), q = 0;
for(int i=1,opt; i<=m; ++i){
opt = readint();
if(opt == 1){
++ stamp;
cmd[stamp].x = readint();
cmd[stamp].a = readint();
}
else{
ask[++ q][0] = readint();
ask[q][1] = stamp; // time
}
}
rep(i,1,n) tim[id[i] = i] = stamp+1;
solve(1,stamp,1,n);
rep(i,1,n) id[i] = i;
std::sort(id+1,id+n+1,cmp);
for(int i=1,j=1; i<=q; ++i){
for(; j!=n+1&&tim[id[j]]<=ask[i][1]; ++j)
BIT::modify(Graph::dfn[id[j]],n);
writeint(BIT::query(Graph::ed[*ask[i]])
-BIT::query(Graph::dfn[*ask[i]]-1));
putchar('\n');
}
return 0;
}