トピックリンクします。https://ac.nowcoder.com/acm/contest/1099/I
タイトル説明
Nは、重み付けされたツリー点を有し、点が2,1番号、...、Nツリーを有している(N - 1)。側部、木を求めて2点間の距離は、数2019の複数の点です?
問題の解決策
カウント点距離:パーティションポイント
キーのポイント校正パーティション機能が変更され、実質的に変化していない他。どのように柔軟な使用の校正機能は、主にいくつかの変数の意味を理解します。パーティションポイントの手順は、重心は、常に探して、とされている重力の中心に各点から得られた<1> </ 2>、一方、一時アレイレコードとこれらの距離は、各サブツリーパーティション、同じ手順。カルは、変更方法、動作するように、<1>、温度を計算した後の関数である特定のトピックに依存します。(重力の着信中心が添加され、ゼロがあるであろう)注点は、<1>カウントも重心にすべてのポイントから動作しますであり、それは必然的に(LENパラメータが計算値である)lenの温度を含むであろう。計算が完了した後、一般的に他の点から点への距離計算値、中間体の処理中に、これらの点の距離です。
calの変更方法
int cc[2019];
inline int calc(int x,int len)
{
dis[x] = len;
temp[0] = 0;//temp[0]记录temp数组的长度
dfs(0,x);
memset(cc,0,sizeof(cc));
for(int i=1;i<=temp[0];i++)
cc[temp[i]%2019]++;
cc[0]--;
int res=cc[0]*(cc[0]+1)/2;
for(int i=1;i<=1009;i++)
res+=cc[i]*cc[2019-i];
return res;
}
重心位置に設定され、それはモジュロ処理Bを考えることは容易である2019の倍数、A-> B + B-> C = 2019、 それはA-> Cは、2019の倍数であることを示しています。
実際には、ダブルカウント、このようなAの問題があります。実例として以下に対処する方法を説明します。
重心は、各点の距離に重心があったが、CAL B->距離A及びCによって機能する> Aも結合され、この可能性を排除する必要があり、我々はこれを終了ごとに記録されています分割統治異なるサブツリーとの間の距離、及び排除する必要性との間の同一のサブ点の木。、ans- = CAL(H、C除外する方法 ) Hを介してHプラス点Cまでの距離のサブノードに、答えは除外される必要があります。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false)
const int N = 20020, INF = 0x7f7f7f7f;
int n,head[N*2],num,tot,ans;//tot记录当前子树点数
int dis[N],flag[N],temp[N];
//dis记录子树每一点到根节点的距离,flag用于删除根节点,temp总汇到根节点的距离
int size[N],Max[N],root;
struct edge
{
int next,to,len;
} G[N*2];
void add(int from,int to,int len)
{
G[++num].next=head[from];
G[num].to=to;
G[num].len=len;
head[from]=num;
}
inline void input(void)
{
for (int i=1; i<n; i++)
{
int x,y,v;
cin>>x>>y>>v;
add(x,y,v), add(y,x,v);
}
}
inline void dp(int fa,int cur)//求树的重心
{
size[cur] = 1, Max[cur] = 0;
for (int i=head[cur]; i; i=G[i].next)
{
int v = G[i].to;
if ( flag[v] || v == fa ) continue;
dp(cur,v);
size[cur] += size[v];
Max[cur] = max( Max[cur], size[v] );
}
Max[cur] = max( Max[cur], tot - size[cur] );
if ( Max[root] > Max[cur] ) root = cur;
}
inline void dfs(int fa,int cur)
{
temp[ ++temp[0] ] = dis[cur];
for (int i=head[cur]; i; i=G[i].next)
{
int v = G[i].to;
if ( v == fa || flag[v] ) continue;
dis[v] = dis[cur] + G[i].len;
dfs(cur,v);
}
}
int cc[2019];
inline int calc(int x,int len)
{
dis[x] = len;
temp[0] = 0;//temp[0]记录temp数组的长度
dfs(0,x);
memset(cc,0,sizeof(cc));
for(int i=1;i<=temp[0];i++)
cc[temp[i]%2019]++;
cc[0]--;
int res=cc[0]*(cc[0]+1)/2;
for(int i=1;i<=1009;i++)
res+=cc[i]*cc[2019-i];
return res;
}
inline void divide(int x)
{
flag[x] = true;//删去根节点
ans += calc(x,0);
//cout<<"ans="<<ans<<"\n";
for (int i=head[x]; i; i=G[i].next)
{
int y = G[i].to;
if ( flag[y] ) continue;
ans -= calc(y,G[i].len);//点对在同一子树的情况
tot = size[y], root = 0;
dp(0,y);
divide(root);
}
}
inline void reset(void)
{
num = 0;
memset( head, 0, sizeof head );
memset( flag, 0, sizeof flag );
ans = 0, tot = n;
root = 0, Max[0] = INF;
}
int main(void)
{
IOS;
while(cin>>n)
{
reset();
input();
dp(0,1);
divide(root);
cout<<ans<<"\n";
//cout<<"\n";
}
return 0;
}