タイトル
質問の意味:
kで割らツリー内の任意の2点間の距離を計算するために必要なKを与えるために、ツリーを与え(切り上げ)と。
分析:
ツリーは問題があるので、とても自然に親ノードの子ノードを転送するのだと思います。我々は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;
}