[ACNOI2022]树界降临

98 篇文章 0 订阅
8 篇文章 0 订阅

题目

题目背景
“当血月出现之时,标杆之力 的拥有者靠近它时会睁开 孤独之眼,” D D ( X Y X ) \sf DD(XYX) DD(XYX) 的额头上,血色的第三只眼慢慢张开,“「无限月独」!”

大地被照得雪亮。这光能穿过影子。所有人都无一例外,进入了像月亮一般孤独的世界。

“「神丶树界降临」!”

题目描述
面前是一棵巨大的神树。所谓树,即 ( n − 1 ) (n{\rm-}1) (n1) 条边、 n n n 个点的连通图。这树可以吸取「卷坷垃」。对于 x x x 号节点,若其吸收了 a a a 份「卷坷垃」,则到它距离为 d    ( d ⩾ 1 ) d\;(d\geqslant 1) d(d1) 的节点也会获得 ⌊ 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=2d1a÷2=(2d1a)÷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} 2k1

类似换根 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值