• 两道树链剖分题目


    2020 CCPC-Wannafly Winter Camp Day2 --- F. 采蘑菇的克拉莉丝

    题意:

    解法:

    考虑暴力解法,枚举起点的所有出边,拿边权乘以子树中蘑菇总数。最坏复杂度(O(nq))
    考虑轻重链剖分,每个点只考虑连向父亲的边的贡献,连向重儿子的边的贡献,所有轻儿子的贡献我们用一个tag记录下来。连向父亲的边贡献和连向重儿子的边都可以直接计算。
    下面考虑所有轻儿子的贡献。修改点u的蘑菇数量,只可能对他的所有祖先结点的轻链答案有影响(对于一个点v不是u的祖先,那么u对v的贡献一定在指向父亲的边里计算了)。
    那么我们从u向上跳重链,对轻链到达的所有点打上贡献标记即可,这样每次修改复杂度(O(logn))
    我们通过线段树维护子树内蘑菇大小就可以在(O(logn))的复杂度内求出连向父亲和重儿子的边的贡献。

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    
    const int maxn = 1e6;
    int n;
    ll bit[maxn + 11];
    int son[maxn + 11],siz[maxn + 11],id[maxn + 11],top[maxn + 11],fa[maxn + 11],weight[maxn + 11];
    vector <pair<int,int> > edge[maxn + 11];
    ll val[maxn + 11];
    
    void dfs(int x,int f) {
    	fa[x] = f;
    	siz[x] = 1;
    	for (auto pi : edge[x]) {
    		int v = pi.first;
    		if (v == f) continue;
    		weight[v] = pi.second;
    		dfs(v , x);
    		siz[x] += siz[v];
    		if (siz[v] > siz[son[x]]) son[x] = v;
    	} 
    } 
    
    int tot = 0;
    void dfs2(int x,int t) {
    	top[x] = t;
    	id[x] = ++tot;
    	if (!son[x]) return;
    	dfs2(son[x] , t);
    	for (auto pi : edge[x]) {
    		int v = pi.first;
    		if (v == fa[x] || v == son[x]) continue;
    		dfs2(v , v);
    	}
    }
    
    int lowbit(int x) { return x & (-x); }
    void update(int x,int val) { for (; x <= n; x += lowbit(x)) bit[x] += val; }
    ll sum(int x) {
    	ll ans = 0;
    	for ( ; x ; x -= lowbit(x)) ans += bit[x];
    		return ans;
    }
    ll query(int l,int r) { return sum(r) - sum(l - 1); }
    
    ll calc(int v) { 
    	ll ans = val[v];
    	if (son[v]) ans += query(id[son[v]] , id[son[v]] + siz[son[v]] - 1) * weight[son[v]];
    	if (v != 1) ans += (query(1 , n) - query(id[v] , id[v] + siz[v] - 1)) * weight[v];
    	return ans;
    } 
    
    int main(){
    	
    	scanf("%d" , &n);
    	for (int i = 1; i < n; i++) {
    		int u,v,w;
    		scanf("%d %d %d",&u,&v,&w);
    		edge[u].push_back(make_pair(v , w));
    		edge[v].push_back(make_pair(u , w));
    	}
    	dfs(1 , 0);
    	dfs2(1 , 1);
    	int q;
    	scanf("%d" , &q);
    	int s = 1;
    	top[1] = 0;
    	while (q--) {
    		int op;
    		scanf("%d" , &op);
    		if (op == 1) {
    			int v,x;
    			scanf("%d %d",&v,&x);
    			update(id[v] , x);
    			while (v != 1) {
    				if (v == top[v]){
    					val[fa[v]] += 1ll * weight[v] * x;
    					v = fa[v];
    				}
    				else v = top[v];
    			}
    		} 
    		else { 
    			int v;
    			scanf("%d" , &v);
    			s = v;
    		} 
    		printf("%lld
    " , calc(s));
    	} 
    } 
    
    

    Codeforces 1254D Tree Queries

    题意:

    给定一棵树,每次进行两种操作,第一种操作给一个点v和一个值d,等概率从所有点中选一点r,对于所有u,若u->r的路径经过v,就将u的权值加上d。第二种操作求点v的期望权值。

    解法:

    对于每个操作一,我们可以推出,对于所有子树v外面的点,当r选在子树v内就可以通过v,所以期望增加(frac {d}{n}*siz(v));对于所有在子树v内的点,考虑在v的儿子y的子树中,那么
    r选在v子树外或者v除了儿子y其他儿子的子树内都可以通过v,所以期望增加(frac {d}{n}*(n-siz(y)))
    暴力做法,枚举v的所有儿子,对子树进行修改。
    和上面一道题目类似,我们可以进行优化。同样只对重儿子进行修改,轻儿子统一计算。对于每个修改v,我们只会对他的子辈结点期望产生影响。(其他结点都在子树外,可以统一计算)这样我们对v的重儿子所对应的子树进行(O(logn))的线段树暴力修改,再将权值d记录在v结点上。统计答案时,每个结点的答案就是线段树记录的答案加上另外一部分,这部分就是这个结点向上跳遇到的所有轻链连接着的父亲上的tag所产生的答案。跳轻重链复杂度也是(O(logn))

    #include <bits/stdc++.h>
    #define lson rt << 1
    #define rson rt << 1 | 1
    #define ll long long
    using namespace std;
    const ll mol = 998244353;
    const int maxn = 150000;
    ll tree[4 * maxn + 11],lazy[4 * maxn + 11];
    ll tag[maxn + 11];
    int siz[maxn + 11],f[maxn + 11],id[maxn + 11],top[maxn + 11],son[maxn + 11];
    vector <int> edge[maxn + 11];
    
    int tot = 0;
    ll qpow(ll a,ll b) {
    	ll ans = 1;
    	while (b) {
    		if (b & 1) ans = ans * a % mol;
    		a = a * a % mol;
    		b >>= 1;
    	}
    	return ans;
    }
    
    void dfs(int x,int fa) {
    	siz[x] = 1;
    	f[x] = fa;
    	for (auto v : edge[x]) {
    		if (v == fa) continue;
    		dfs(v , x);
    		siz[x] += siz[v];
    		if (siz[v] > siz[son[x]]) son[x] = v;
    	}
    } 
    
    void dfs2(int x,int t) {
    	top[x] = t;
    	id[x] = ++tot;
    	if (son[x]) dfs2(son[x] , t);
    	for (auto v : edge[x]) {
    		if (v == f[x] || v == son[x]) continue;
    		dfs2(v , v);
    	} 
    }
    
    ll add(ll a,ll b) { a += b; if (a >= mol) a -= mol; return a; }
    ll sub(ll a,ll b) { a -= b; if (a < 0) a += mol; return a; }
    
    void push_up(int rt) { tree[rt] = add(tree[lson] , tree[rson]); }
    void push_down(int rt,int l,int r) {
    	int mid = (l + r) >> 1;
    	ll val = lazy[rt]; lazy[rt] = 0;
    	tree[lson] = add(tree[lson] , val * (mid - l + 1) % mol); lazy[lson] = add(lazy[lson] , val);
    	tree[rson] = add(tree[rson] , val * (r - mid) % mol); lazy[rson] = add(lazy[rson] , val);
    }
    
    void update(int rt,int l,int r,int al,int ar,ll val) {
    	if (l > ar || r < al) return;
    	if (l >= al && r <= ar) {
    		tree[rt] = add(tree[rt] , val * (r - l + 1) % mol);
    		lazy[rt] = add(lazy[rt] , val);
    		return;
    	}
    	if (lazy[rt]) push_down(rt , l , r);
    	int mid = (l + r) >> 1;
    	update(lson , l , mid , al , ar , val);
    	update(rson , mid + 1 , r , al , ar , val);
    	push_up(rt);
    } 
    
    ll query(int rt,int l,int r,int pos) {
    	if (l == r) return tree[rt];
    	if (lazy[rt]) push_down(rt , l , r);
    	int mid = (l + r) >> 1;
    	if (mid >= pos) return query(lson , l , mid , pos);
    	return query(rson , mid + 1 , r , pos);
    }
    
    int main() {
    	int n,q;
    	scanf("%d %d",&n,&q);
    	for (int i = 1; i < n; i++) {
    		int u,v;
    		scanf("%d %d",&u,&v);
    		edge[u].emplace_back(v);
    		edge[v].emplace_back(u);
    	}
    	dfs(1 , 0); 
    	dfs2(1 , 1);
    	ll all = 0;
    	ll inv = qpow(n , mol - 2);
    	while (q--) {
    		int op,v;
    		scanf("%d %d",&op,&v);
    		if (op == 1) {
    			int d;
    			scanf("%d" , &d);
    			tag[v] = add(tag[v] , d);
    			all = add(all , inv * d % mol * siz[v] % mol);
    			if (son[v]) update(1 , 1 , n , id[son[v]] , id[son[v]] + siz[son[v]] - 1 , inv * d % mol * sub(n - siz[v] , siz[son[v]]) % mol);
    		}
    		else {
    			ll ans = add(query(1 , 1 , n , id[v]) , inv * tag[v] % mol * (n - siz[v]) % mol);
    			while (v) {
    				v = top[v];
    				ans = add(ans , tag[f[v]] * inv % mol * sub(n - siz[v] , siz[f[v]]) % mol);
    				v = f[v];
    			}
    			printf("%lld
    " , add(ans , all));
    		}
    	}
    } 
    
    
  • 相关阅读:
    史上最强大vimrc
    Linux 宿主目录、根目录及/home区别
    ubuntu配置软件源
    Lex入门2
    域名服务器(DNS)工作原理
    SQL Server 2005脚本编辑窗口不能使用Enter,Backspace, Insert等按键
    建立windows2003 域名服务器
    JavaScript操作cookie
    VS2008下设置断点调试JavaScript (IE)
    DNS域名服务器原理与架设(Bind on Linux)
  • 原文地址:https://www.cnblogs.com/Embiid/p/12274213.html
Copyright © 2020-2023  润新知