兰州大学第一届 飞马杯 ★★快乐苹果树★★ 树链剖分 + 懒标记 + 树状数组

传送门

文章目录

题意:

在这里插入图片描述

思路:

第一次听说树链剖分能在 f a [ t o p [ i ] ] fa[top[i]] fa[top[i]]的地方加懒标记,学到了学到了。

首先不能被题目吓住,这个题目仔细剖析一下不难发现一些性质:
以下用 s e i se_i sei表示 i i i子树的大小。
( 1 ) (1) (1) u u u选择了 f f f点,那么 v v v选任一点都可以,也就是让 f f f为根的子树的每个点权值都加 1 s e f 1 s e f k \frac{1}{se_f}\frac{1}{se_f}k sef1sef1k
( 2 ) (2) (2) u u u选择了非 f f f点的时候,其选择的点假设落在与 f f f直接相连的 x x x点所在的子树中,那么 v v v只能选非 x x x的子树中的点,也就是让非 x x x的子树的点都加上 s e x s e f 1 s e f − s e x k \frac{se_x}{se_f}\frac{1}{se_f-se_x}k sefsexsefsex1k
以上两个操作显然可以先将 f f f所在的子树都加上 1 s e f 1 s e f k + ∑ s e x s e f 1 s e f − s e x k \frac{1}{se_f}\frac{1}{se_f}k+\sum \frac{se_x}{se_f}\frac{1}{se_f-se_x}k sef1sef1k+sefsexsefsex1k,现在就有个比较显然的做法就是遍历 f f f的出边给子树减去 s e x s e f 1 s e f − s e x k \frac{se_x}{se_f}\frac{1}{se_f-se_x}k sefsexsefsex1k f f f的全部子树都加上 1 s e f 1 s e f k + ∑ s e x s e f 1 s e f − s e x k \frac{1}{se_f}\frac{1}{se_f}k+\sum \frac{se_x}{se_f}\frac{1}{se_f-se_x}k sef1sef1k+sefsexsefsex1k即可。
但这样的话给你个菊花图就炸了,复杂度升到 q n l o g n qnlogn qnlogn,由于是对子树操作的,比较容易想到能不能加个懒标记呢?我们加了懒标记又如何在计算答案的时候能减去这个懒标记呢?解决了这个两个问题我们就可以顺利解决这个问题了。
树上倍增解决这个题不是很容易,我们考虑重链剖分。
首先我们加点的时候,思路就是按照上面哪个思路,即先给所有点都加上,再给子树减去某个值,所以我们 l a z y lazy lazy标记应该是标记了减去了多少。首先加操作显然可以写一个 d f s dfs dfs序,让后用树状数组维护一下。考虑书剖的特殊性质,我们只需要在 t a g [ f ] tag[f] tag[f]加上 k k k,让后将重儿子直接减去 s e x s e f 1 s e f − s e x k \frac{se_x}{se_f}\frac{1}{se_f-se_x}k sefsexsefsex1k,因为重儿子在之后跳 t o p top top的时候会直接跳过的,而轻儿子在跳到这条链的 t o p [ i ] top[i] top[i]的时候,需要减去 s 2 [ t o p [ i ] ] ∗ t a g [ f a [ t o p [ i ] ] ] s2[top[i]]*tag[fa[top[i]]] s2[top[i]]tag[fa[top[i]]] s 2 s2 s2是预处理的 s e x s e f 1 s e f − s e x \frac{se_x}{se_f}\frac{1}{se_f-se_x} sefsexsefsex1
这样问题就顺利解决啦~

// Problem: ★★快乐苹果树★★
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/16520/K
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid (tr[u].l+tr[u].r>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
#define lowbit(x) ((x)&(-x))
using namespace std;

//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;

const int N=200010,mod=998244353,INF=0x3f3f3f3f;
const double eps=1e-6;

int n,m;
vector<int>v[N];
int se[N],son[N],top[N],fa[N],in[N],tot;
LL inv[N],s1[N],s2[N],s3[N];
LL tr[N],tag[N];

void dfs1(int u,int f) {
	se[u]=1; fa[u]=f;
	for(auto x:v[u]) {
		if(x==f) continue;
		dfs1(x,u);
		se[u]+=se[x];
		if(se[son[u]]<se[x]) son[u]=x;
	}
	s1[u]=inv[se[u]]*inv[se[u]]%mod;
	for(auto x:v[u]) {
		if(x==f) continue;
		s2[x]=se[x]*inv[se[u]]%mod*inv[se[u]-se[x]]%mod;
		s3[u]+=s2[x]; s3[u]%=mod;
	}
}

void dfs2(int u,int t) {
	top[u]=t; in[u]=++tot;
	if(son[u]) dfs2(son[u],t); 
	for(auto x:v[u]) {
		if(x==fa[u]||x==son[u]) continue;
		dfs2(x,x);
	}
}

void add(int x,LL c) {
	for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c,tr[i]%=mod,tr[i]+=mod,tr[i]%=mod;
}

LL query(int x) {
	LL ans=0;
	for(int i=x;i;i-=lowbit(i)) ans+=tr[i],ans%=mod;
	return ans;
}

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);

	inv[1]=1;
	for(int i=2;i<N;i++)
	    inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	int _; scanf("%d",&_);
	while(_--) {
		scanf("%d%d",&n,&m); tot=0;
		for(int i=1;i<=n;i++) se[i]=son[i]=top[i]=fa[i]=s3[i]=s2[i]=s1[i]=0,v[i].clear();
		for(int i=1;i<=n-1;i++) {
			int a,b; scanf("%d%d",&a,&b);
			v[a].pb(b); v[b].pb(a);
		}
		dfs1(1,0); dfs2(1,1);
		while(m--) {
			int op,x; scanf("%d%d",&op,&x);
			if(op==1) {
				int k; scanf("%d",&k);
				add(in[x],1ll*(s1[x]+s3[x])%mod*k%mod); add(in[x]+se[x],(((mod-s1[x]-s3[x])%mod+mod)%mod)*k%mod);
				if(son[x]) add(in[son[x]],(mod-s2[son[x]]*k%mod)%mod),add(in[son[x]]+se[son[x]],s2[son[x]]*k%mod);
				tag[x]+=k; tag[x]%=mod;
			} else {
				LL ans=query(in[x]);
				for(int i=top[x];fa[i];i=top[fa[i]]) {
					ans-=tag[fa[i]]*s2[i]%mod,ans+=mod,ans%=mod;
				}
				printf("%lld\n",ans%mod);
			}
		}
		for(int i=1;i<=n;i++) tr[i]=tag[i]=0;
	}



	return 0;
}
/*

*/









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值