C.ベアとツリーがジャンプ(DP +ツリーのルート変更)

タイトル

質問の意味:

    kで割らツリー内の任意の2点間の距離を計算するために必要なKを与えるために、ツリーを与え(切り上げ)と。
     2 n個 200 000 1 K 5 2≤N≤200 000、1≤K≤5

分析:

    ツリーは問題があるので、とても自然に親ノードの子ノードを転送するのだと思います。我々はNUM [I] [j]は、iはjのkにノードのサブツリーを表す維持するように、kは、小さいためするノードの距離係数です。そして、親ノードに子ノードの転送に加え、実際には、限り、それらの弾性係数kが長い更新NUMに親ノードの残りの部分のように、親ノードとコストの親ノードに子ノードを追加するために、現在のニーズにコスト0ポイントであるとして、配列は、することができます。だから我々は、ルートノードの寄与を計算することができること。
    我々はルートを変更する必要があるので、他のポイントの寄与を計算するため。換言すれば、その子に現在のノードの寄与を減算する親ノードアレイNUMのルートである場合、親ノードに加えNUMアレイの子ノードは、親ノードカウントアレイ型NUM点kが0であり、親ノードの寄与拠出金(元の親は今の子ノードになります)。復元するときに戻って覚えておいてください!

#include <iostream>
#include <vector> 
using namespace std;

typedef long long ll;

ll dp[200005],num[200005][5];
int n,k;
vector<int> g[200005]; 

void dfs1(int x,int fa)
{
	if( g[x].size() == 1 && g[x][0] == fa )
	{
		return;
	}
	for (int i = 0; i < g[x].size(); i++)
	{
		int t = g[x][i];
		if( t == fa ) continue;
		dfs1(t,x);
		for (int j = 0; j < k; j++)
		{
			num[x][j] += num[t][(j-1+k)%k];
		}
		num[x][1%k] ++;
		dp[x] += dp[t] + 1 + num[t][0];
	}
}

void dfs2(int x,int fa)
{
	for (int i = 0; i < g[x].size(); i++)
	{
		int t = g[x][i];
		if( t == fa ) continue;
		for (int j = 0; j < k; j++)
		{
			num[x][j] -= num[t][(j-1+k)%k];
		}
		num[x][1%k] --;
		dp[t] += dp[x] - dp[t] - 1 - num[t][0];
		dp[t] += 1 + num[x][0];
		for (int j = 0; j < k; j++)
		{
			num[t][j] += num[x][(j-1+k)%k];
		}
		num[t][1%k] ++;
		dfs2(t,x);
		num[t][1%k] --;
		for (int j = 0; j < k; j++)
		{
			num[t][j] -= num[x][(j-1+k)%k];
		}
		num[x][1%k] ++;
		for (int j = 0; j < k; j++)
		{
			num[x][j] += num[t][(j-1+k)%k];
		}
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n >> k;
	for (int i = 1; i < n; i++)
	{
		int x,y;
		cin >> x >> y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	dfs1(1,0);
	dfs2(1,0);
	ll ans = 0;
	for (int i = 1; i <= n; i++)
	{
		ans += dp[i];
	}
	cout << ans / 2 << '\n';
	return 0;
}

公開された132元の記事 ウォンの賞賛6 ビュー7911

おすすめ

転載: blog.csdn.net/weixin_44316314/article/details/105116341