[NOIP2017模拟]树

来源:互联网 发布:我的世界ipad枪械js 编辑:程序博客网 时间:2024/05/21 06:24

题目背景
SOURCE:NOIP2015-HN-CJZX

题目描述
树(tree)是指由 n 个点,n-1 条边构成的连通无向图。如果有一棵树,它的每一条边 (u,v) 都有一个权值 lu,v ,我们把这样的树称作带权树(weighted tree)。

我们知道对于树上的任意两个点,他们之间的路径是唯一的。对于两个点 u,v 来说,我们可以算出 u 与 v 之间的路径上所有边权之和,将其称作 u 与 v 之间路径的长度,记作 d(u,v)。

你的任务是计算:
这里写图片描述

输入格式
输入第一行为一个正整数 n 。
接下来 n-1 行,每行三个非负整数 x,y,w,表示点 x 与点 y 之间有一条权值为 w 的一条边。
保证输入的图是一棵树。

输出格式
输出仅包含一行一个数,即答案。因为结果可能很大,请将答案模 100 000 007 输出。

样例数据 1
输入
4
1 2 4
1 3 4
1 4 4
输出
72
样例数据 2
输入
10
1 2 1320321
2 3 4687651
3 4 1321357
4 5 6651332
5 6 5746513
6 7 5674687
7 8 7451216
8 9 7789965
9 10 8765134
输出
28244404

备注
【样例1说明】
d(1,2)+d(1,3)+d(1,4)=4+4+4=12
d(2,1)+d(2,3)+d(2,4)=4+8+8=20
d(3,1)+d(3,2)+d(3,4)=4+8+8=20
d(4,1)+d(4,2)+d(4,3)=4+8+8=20
所以答案为:12+20+20+20=72。

【数据范围】
共 20 个测试点,其数据特征如下表所示。
这里写图片描述
对所有数据,有 1n3×1050w<100000007。数据有一定梯度。

分析:对于每一条边,使用的次数相当于这条边两边两块中的点数的乘积(左右每两个点都要计算距离对吧)再乘二(双向算距离),所以只需要知道每条边两边有多少点就行了,用个dfs预处理每一棵子树中的点的个数,连接这棵子树的边就要用size[子树点]*size[子树以外的点]次。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<ctime>#include<cmath>#include<algorithm>#include<cctype>#include<iomanip>#include<queue>#include<set>using namespace std;int getint(){    int f=1,sum=0;    char ch;    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());    if(ch=='-')    {        f=-1;        ch=getchar();    }    for(;ch>='0'&&ch<='9';ch=getchar())        sum=(sum<<3)+(sum<<1)+ch-48;    return sum*f;}const int maxn=300010;const int mo=100000007;struct node{    long long w;    int fro,to;}bian[maxn];int tot,n;int first[maxn],nxt[maxn*2],to[maxn*2],size[maxn];long long ans;bool visit[maxn];void addedge(int x,int y,int z){    tot++;    nxt[tot]=first[x];    first[x]=tot;    to[tot]=y;    tot++;    nxt[tot]=first[y];    first[y]=tot;    to[tot]=x;}void dfs(int u)//计算子树点数{    for(int p=first[u];p;p=nxt[p])    {        int v=to[p];        if(!visit[v])        {            visit[v]=1;            dfs(v);            visit[v]=0;            size[u]+=size[v];        }    }}int main(){    freopen("tree.in","r",stdin);    freopen("tree.out","w",stdout);    n=getint();    for(int i=1;i<=n;++i)        size[i]=1;//子树包含子树的根节点    int x,y,z;    for(int i=1;i<n;++i)    {        x=getint();y=getint();z=getint();        bian[i].fro=x;bian[i].to=y,bian[i].w=z;//不要问我为什么要赋两遍值,只是考试的时候傻了罢了        addedge(x,y,z);    }    visit[1]=1;    dfs(1);    for(int i=1;i<n;++i)        if(size[bian[i].fro]>size[bian[i].to])//还是要判断一下谁是父亲,不然size就找错了            ans=(ans+(bian[i].w*size[bian[i].to]*(size[1]-size[bian[i].to]))%mo)%mo;        else            ans=(ans+(bian[i].w*size[bian[i].fro]*(size[1]-size[bian[i].fro]))%mo)%mo;    ans=(ans*2)%mo;//乘二的工作最后来    cout<<ans<<'\n';    return 0;}

本题结。

原创粉丝点击