zoukankan      html  css  js  c++  java
  • [LCA]求和(bzoj5293)

    基情链接

    解题思路

    LCA没什么好说的,对于这类树上路径的题向来lca比较好处理。值得一提的是,k次方是各边分别k次方。由于k比较小,其实就不用管他,处理k棵树即可。

    一句话题意:求树上两个点之间的所有点“深度”之和。

    我们就可以分解一下这一段路径,其实就是a到lca与b到lca,我们用一个前缀和,储存,差分思想再相减即可。

    LCA被多减了1次,我们需加回来。

    还有,此题需要取模,还是比较坑。

    对于求LCA,我们有许多方法,这里就有大家喜闻乐见的暴力,即在相同深度,一起往上爬,直到走到相同点。同时还有倍增,即,每次成倍爬树,找到不相等的最小深度的点,在走一步即到了LCA,第三种方法就是tarjan。有一个好处是可以直接求出所有,就非常好用。

    暴力通常很慢,这道题我用的倍增。

    定义dp[i][j]表示从i点向上走2^j步所到达的点。

    显然有dp[i][j] = dp[dp[i][j - 1]][j - 1]

    其实就相当于把2^j拆成先走2^j - 1 再走2^j - 1

    给出一个模板

    void dfs(int step){
    	vis[step] = 1;
    	for (int i = 0;i < G[step].size();i ++){
    		if (!vis[G[step][i]]){
    			dep[G[step][i]] = dep[step] + 1;
    			dp[G[step][i]][0] = step;
    			dfs(G[step][i]);
    		}
    	}
    }
    void build(){
    	dep[1] = 0;
    	dfs(1);
    	dp[1][0] = 1;
    	for (int i = 1;i <= 31;i ++){
    		for (int j = 1;j <= n;j ++){
    			dp[j][i] = dp[dp[j][i - 1]][i - 1];
    		}
    	}
    }
    void mov(int &u,int v){
    	for (int i = 30;i >= 0;i --){
    		if (dep[dp[u][i]] >= v)
    			u = dp[u][i];
    	}
    }
    int solve(int u,int v){
    	if (dep[u] < dep[v])
    		mov(v,dep[u]);
    	if (dep[u] > dep[v])
    		mov(u,dep[v]);
    	if (u == v)
    		return u;
    	for (int i = 30;i >= 0;i --){
    		if (dp[u][i] != dp[v][i]){
    			u = dp[u][i];
    			v = dp[v][i];
    		}
    	}
    	return dp[u][0];
    }

    dfs其实就是预处理出dp初值以及深度,因为我们的在线LCA都是要在同一深度进行操作。

    同时如果刚开始深度不相同,我们则需要用mov移动到相同深度,然后开始爬树。

    我们采取的方式是一直往上面爬,如果爬到的点相同,则不一定是LCA,不一定是最近的公共祖先,因此,我们一直爬到不相同的点,最后,往上面再爬一下即是LCA。

    这道题再加一个前缀和以及处理k棵树即可。耗时比较久,应该有些优化。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<queue>
    #include<vector>
    #include<algorithm>
    #define mod 998244353
    using namespace std;
    int n,m;
    bool vis[300005];
    int dp[300005][31],dep[300005];
    long long sum[300005][55];
    vector <int>G[300005];
    long long qkpow(long long x,long long y){
    	long long ans = 1;
    	while (y){
    		if (y & 1)
    			ans = ans * x % mod;
    		x = x * x % mod;
    		y /= 2;
    	}
    	return ans;
    }
    void dfs(int step){
    	vis[step] = 1;
    	for (int i = 0;i < G[step].size();i ++){
    		if (!vis[G[step][i]]){
    			dep[G[step][i]] = dep[step] + 1;
    			for (int j = 1;j <= 50;j ++)
    				sum[G[step][i]][j] = (qkpow(dep[G[step][i]],j)+ sum[step][j]) % mod;
    			dp[G[step][i]][0] = step;
    			dfs(G[step][i]);
    		}
    	}
    }
    void build(){
    	/*for (int i = 2;i <= 50;i ++)
    		sum[1][i] = 1;*/
    	dfs(1);
    	dp[1][0] = 1;
    	for (int j = 1;j <= 20;j ++){
    		for (int i = 1;i <= n;i ++){
    			dp[i][j] = dp[dp[i][j - 1]][j - 1];
    		}
    	}
    }
    void mov(int &u,int v){
    	for (int i = 20;i >= 0;i --){
    		if (dep[dp[u][i]] >= dep[v]){
    			u = dp[u][i];
    		}
    	}
    }
    int solve(int u,int v){
    	if (dep[u] > dep[v])
    		mov(u,v);
    	if (dep[u] < dep[v])
    		mov(v,u);
    	if (u == v)
    		return u;
    	for (int i = 20;i >= 0;i --){
    		if (dp[u][i] != dp[v][i]){
    			u = dp[u][i];
    			v = dp[v][i];
    		}
    	}
    	return dp[u][0];
    }
    int main(){
    	scanf ("%d",&n);
    	for (int i = 1;i < n;i ++){
    		int a,b;
    		scanf ("%d%d",&a,&b);
    		G[a].push_back(b);
    		G[b].push_back(a);
    	}
    	build();
    	scanf("%d",&m);
    	while (m --){
    		int x1,x2,k;
    		scanf ("%d%d%d",&x1,&x2,&k);
    		int lca = solve(x1,x2);
    		printf("%lld
    ",(sum[x1][k] - sum[lca][k] + sum[x2][k] - sum[dp[lca][0]][k] + 2 * mod ) % mod);
    	}
    }
  • 相关阅读:
    洞察僵尸网络的4条关键线索,你知道吗?
    数据即服务(DaaS)的好处和趋势
    AIOT:什么是智联网,它是未来吗?
    渐变略过效果
    页面头部banner动画效果
    小三角
    监测屏幕宽度
    开关效果
    高级轮播
    手机端跳转页面指定楼层
  • 原文地址:https://www.cnblogs.com/lover-fucker/p/13566676.html
Copyright © 2011-2022 走看看