首页 > 其他分享 >换根DP

换根DP

时间:2023-11-22 22:13:14浏览次数:26  
标签:nxt cur int dfs 换根 DP 节点 dp

换根DP

又称二次扫描

特征:

  1. 树中没有指定根节点。
  2. 采用不同的节点作为根,答案可能不一样。

模板

P3478 [POI2008] STA-Station

暴力解法:枚举根节点,求以该节点作为根时,所有节点的深度之和,时间复杂度O(n^2)
优化:直接通过父节点的深度之和,得到子节点的深之和:子节点变为根节点之后,子节点及其子树深度+1,其他节点深度-1。
解法:

  1. 先指定任意节点作根节点
  2. 搜索完成指定根的答案计算,得到指定根的解。
  3. 二次扫描,利用原来根的解推出每个节点作为根的解。

定义状态

dp[i]表示以1为总根节点,i为根的子树所有节点的深度之和
f[i]表示以i为总根节点,其他节点的深度之和。
答案:1~n中f[i]的最大值

求状态转移方程

dp[cur]+=dp[nxt]
f[cur]=f[fa]-siz[cur]+(n-siz[cur])
这个子树的深度全部-1,即减去这个子树的大小;其余节点深度+1(siz[i]为i的子树节点数量)

初始状态

dp[cur]=dep[cur](dep[i]为节点i的深度)
f[root]=dp[root]

验证转移顺序

dp是先递归后转移
f是先转移再递归(f[nxt]=f[cur]-siz[nxt]+(n-siz[nxt]))

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long

const int N=1e6+5;
int n,dp[N],f[N],dep[N],siz[N];
vector<int>nbr[N];

void dfs(int cur,int fa)
{
	dp[cur]=dep[cur]=dep[fa]+1;
	siz[cur]=1;
	for(int i=0;i<nbr[cur].size();i++)
	{
		int nxt=nbr[cur][i];
		if(nxt==fa)
			continue;
		dfs(nxt,cur);
		dp[cur]+=dp[nxt];
		siz[cur]+=siz[nxt];
	}
	return ;
}

void second_dfs(int cur,int fa)
{
	for(int i=0;i<nbr[cur].size();i++)
	{
		int nxt=nbr[cur][i];
		if(nxt==fa)
			continue;
		f[nxt]=f[cur]-siz[nxt]+(n-siz[nxt]);
		second_dfs(nxt,cur);
	}
	return ;
}

signed main()
{
	cin>>n;
	for(int i=1;i<=n-1;i++)
	{
		int x,y;
		cin>>x>>y;
		nbr[x].push_back(y);
		nbr[y].push_back(x);
	}
	dfs(1,0);
	f[1]=dp[1];
	second_dfs(1,0);
	int maxi=-1e9,pos;
	for(int i=1;i<=n;i++)
		if(f[i]>maxi)
		{
			maxi=f[i];
			pos=i;
		}
	cout<<pos;
	return 0;
}

时间复杂度O(n)

CF1187E Tree Painting(双倍经验)

只需将dep初值设为0即可

P2986 [USACO10MAR] Great Cow Gatrering G

本题加了边权和边权,只要稍微改动即可。
dfssecond_dfs代码:

void dfs(int cur,int fa)
{
	siz[cur]=val[cur];
	for(int i=0;i<nbr[cur].size();i++)
	{
		int nxt=nbr[cur][i].to,w=nbr[cur][i].w;
		if(fa==nxt)
			continue;
		dfs(nxt,cur);
		dp[cur]+=dp[nxt]+siz[nxt]*w;
		siz[cur]+=siz[nxt];
	}
	return ;
}
void second_dfs(int cur,int fa)
{
	for(int i=0;i<nbr[cur].size();i++)
	{
		int nxt=nbr[cur][i].to,w=nbr[cur][i].w;
		if(fa==nxt)
			continue;
		f[nxt]=f[cur]-siz[nxt]*w+(tot-siz[nxt])*w;
		second_dfs(nxt,cur);
	}
	return ;
}

CF219D Choosing Capital for Treeland

定义状态

dp[i]表示以1为总根节点,i为根的子树需要反转方向的次数
f[i]表示以i为总根节点,需要反转方向的次数
答案:f[i]的最小值

求状态转移方程

dp[cur]+=dp[nxt]+w
w0 -> f[nxt]=f[cur]+1
w
1 -> f[nxt]=f[cur]-1

初始状态

f[1]=dp[1]

验证转移顺序

dp是先递归后转移
f是先转移再递归

代码

#include<bits/stdc++.h>
using namespace std;

const int N=2e5+5;
struct Node
{
	int to;
	int w;
};
int n,dp[N],f[N];
vector<Node>nbr[N];

void dfs(int cur,int fa)
{
	for(int i=0;i<nbr[cur].size();i++)
	{
		int nxt=nbr[cur][i].to,w=nbr[cur][i].w;
		if(fa==nxt)
			continue;
		dfs(nxt,cur);
		dp[cur]+=dp[nxt]+w;
	}
	return ;
}

void second_dfs(int cur,int fa)
{
	for(int i=0;i<nbr[cur].size();i++)
	{
		int nxt=nbr[cur][i].to,w=nbr[cur][i].w;
		if(nxt<span style="font-weight: bold;" class="mark">fa)
			continue;
		if(w</span>0)
			f[nxt]=f[cur]+1;
		else
			f[nxt]=f[cur]-1;
		second_dfs(nxt,cur);
	}
	return ;
}

int main()
{
	cin>>n;
	for(int i=1;i<=n-1;i++)
	{
		int x,y;
		cin>>x>>y;
		nbr[x].push_back((Node){y,0});
		nbr[y].push_back((Node){x,1});
	}
	dfs(1,0);
	f[1]=dp[1];
	second_dfs(1,0);
	int mini=1e9;
	for(int i=1;i<=n;i++)
		mini=min(mini,f[i]);
	cout<<mini<<"\n";
	for(int i=1;i<=n;i++)
		if(f[i]==mini)
			cout<<i<<" ";
	return 0;
}

标签:nxt,cur,int,dfs,换根,DP,节点,dp
From: https://www.cnblogs.com/hyfly2000/p/change-the-root-dp-z29uype.html

相关文章

  • 【题目-任务安排2】斜率优化dp
    题解首先,递推关系如下:\(dp[i]=min(dp[i],dp[j]+sumt[i]*(sumc[i]-sumc[j])+s*(sumc[n]-sumc[j]));\)显然N太大,无法\(O(n^2)\)算法解决问题。考虑如何优化掉第二个j的循环,发现这个循环是找最优的j位置假设\(j\)就是最优位置,那么可以先初步消掉min,接着如下分析:......
  • DPR Walk
    题意给定一个无向图,求路径长度为\(k\)的路径条数。\(n\le50\)。Sol考虑\(dp\),设\(f_{i,j}\)表示从\(i\toj\)的路径长为\(k\)的方案数。不难发现转移即为矩阵乘法。直接快速幂即可。Code#include<iostream>#include<algorithm>#include<cstdio>#includ......
  • DPS Digit Sum
    题意求\(1\ton\)中有多少个数是\(d\)的倍数。\(n\le10^{10000}\)。Sol数位dp,设\(f_{i,j,1/0}\)表示第\(i\)位,膜\(d\)等于\(j\),是否贴住上限。转移是\(trivial\)的。Code#include<iostream>#include<algorithm>#include<cstdio>#includ......
  • UnhandledPromiseRejectionWarning: SyntaxError: Unexpected token '??=' 报错处理
    在用vite创建react的时候install完成后输入pnpmrundev突然蹦出UnhandledPromiseRejectionWarning:SyntaxError:Unexpectedtoken'??='一脸闷逼,百度了一下。哦吼,逻辑空赋值(??=)是ES2021的语法,nodev15.0.0以上才支持逻辑空赋值(??=)的语法。之前为了兼容旧代码使用的n......
  • dp问题
    1.区间dpP1063[NOIP2006提高组]能量项链-洛谷|计算机科学教育新生态(luogu.com.cn)对于环形问题,我们常常可以采用将n个元素复制成2n个元素,或者选择(i+1)%n的形式第一次遇到区间dp,写个题解总结一下区间dp能解决的问题就是通过小区间更新大区间,最后得出指定区间的最......
  • DPO Matching
    题意给定一张大小为\(2n\)的图,求该图二分图匹配的方案数。\(n\le21\)。Sol状压板题。设\(f_T\)表示\(T\)集合内的点被匹配。直接转移即可。Code#include<iostream>#include<algorithm>#include<cstdio>#include<array>usingnamespacestd;#ifdefONLINE......
  • Modbus 转 PROFIBUS DP 应用场景 PM-160
    1)在网关PROFIBUSDP侧是一个PROFIBUSDP从站,在Modbus串口侧有Modbus主站、Modbus从站、通用模式可选:接口有RS232RS485、RS422三种可选。2)通信方式为半双工:波特率有300~115200bps可选;有/无校验位、奇/偶校验和标记/空格可选。3)网关作为PROFIBUS从站,波特率自适应MaxS12Mbps......
  • centos:subprocess.CalledProcessError: Command ‘[‘ninja‘, ‘-v‘]‘ returned n
    一、原因pytorch版本大于1.5二、解决1、降低pytorch版本将pytorch版本降到1.5以下2、禁用ninjiapytorch默认使用ninjia作为backend,将其禁用。替换为以下代码setup(...,cmdclass={#'build_ext':BuildExtension,'build_ext':BuildExtension.w......
  • 区间dp
    1.acwing282石子合并问题1#include<bits/stdc++.h>2usingnamespacestd;34intn;5constintN=310;6ints[N];7intf[N][N];89intmain()10{11scanf("%d",&n);12for(inti=1;i<=n;i++)scanf(&qu......
  • 线性dp
    1.数字三角形。acwing898.1#include<bits/stdc++.h>2usingnamespacestd;34constintN=520,INF=1e9;5intn;6inta[N][N];//表示每一个点7intf[N][N];//表示状态89intmain()10{11cin>>n;12for(inti=1;i<=n;i......