http://acm.hdu.edu.cn/showproblem.php?pid=3534
题意:给出一颗树(n个顶点,n-1条边)
求最长的路径,以及最长路径的条数。
路径无非就是连接两个点直接的路。
因为是一颗树,所以连接两个点肯定是唯一的路径。
其实就是求两点间距离的最大值,以及这个最大值有多少个。
很裸的树形DP;
首先统计出结点到叶子结点的最长距离和次长距离。
然后找寻经过这个点的,在这个为根结点的子树中的最长路径个数目。
#include
#include
#include
#include
using namespace std;
const int MAXN=100010;
const int INF=0x3f3f3f3f;
struct Node
{
int to,next,len;
}edge[MAXN*2];
int head[MAXN];
int tol;
int maxn[MAXN];//该节点往下到叶子结点的最大距离
int smaxn[MAXN];// 次大距离
int maxn_num[MAXN];//最大距离的个数
int smaxn_num[MAXN];//次大距离的个数
int path[MAXN];//该结点为根的子树中,包含该结点的最长路径长度
int num[MAXN];//最长路径的长度
void init()
{
tol=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v,int w)
{
edge[tol].to=v;
edge[tol].len=w;
edge[tol].next=head[u];
head[u]=tol++;
edge[tol].to=u;
edge[tol].len=w;
edge[tol].next=head[v];
head[v]=tol++;
}
void dfs(int u,int pre)
{
maxn[u]=smaxn[u]=0;
maxn_num[u]=smaxn_num[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==pre)continue;
dfs(v,u);
if(maxn[v]+edge[i].len>maxn[u])
{
smaxn[u]=maxn[u];
smaxn_num[u]=maxn_num[u];
maxn[u]=maxn[v]+edge[i].len;
maxn_num[u]=maxn_num[v];
}
else if(maxn[v]+edge[i].len==maxn[u])
{
maxn_num[u]+=maxn_num[v];
}
else if(maxn[v]+edge[i].len>smaxn[u])
{
smaxn[u]=maxn[v]+edge[i].len;
smaxn_num[u]=maxn_num[v];
}
else if(maxn[v]+edge[i].len==smaxn[u])
{
smaxn_num[u]+=maxn_num[v];
}
}
if(maxn_num[u]==0)//叶子结点
{
maxn[u]=smaxn[u]=0;
maxn_num[u]=smaxn_num[u]=1;
path[u]=0;
num[u]=1;
return;
}
int c1=0,c2=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==pre)continue;
if(maxn[u]==maxn[v]+edge[i].len)c1++;
else if(smaxn[u]==maxn[v]+edge[i].len)c2++;
}
path[u]=0;
num[u]=0;
if(c1>=2)//最长+最长
{
int tmp=0;
path[u]=maxn[u]*2;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==pre)continue;
if(maxn[u]==maxn[v]+edge[i].len)
{
num[u]+=tmp*maxn_num[v];
tmp+=maxn_num[v];
}
}
}
else if(c1>=1 && c2>=1)//最长+次长
{
path[u]=maxn[u]+smaxn[u];
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==pre)continue;
if(maxn[u]==maxn[v]+edge[i].len)
{
num[u]+=maxn_num[v]*smaxn_num[u];
}
}
}
else//最长
{
path[u]=maxn[u];
num[u]=maxn_num[u];
}
}
int main()
{
int n;
while(scanf("%d",&n)==1)
{
int u,v,w;
init();
for(int i=1;ians1)
{
ans1=path[i];
ans2=num[i];
}
else if(path[i]==ans1)
ans2+=num[i];
}
printf("%d %d\n",ans1,ans2);
}
return 0;
}
问树的直径及直径的个数。max[now]
,但是对第三种,如果算为max[now]
的话,那么还会在max[father]
处算一遍,所以max[now]
中不包含父亲上的信息。cnt[now]
有三种情况cnt[now] = 1
cnt[now] = 次长边的个数
cnt[now] = blablabla……
(太长了,,,看代码吧)#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
情况1:由某叶子节点出发产生的最长路径直接构成
情况2:由某有多个儿子的节点出发产生的两条长路径组成,这其中,又可以分为两条长路径长度相等与否两种情况
所以 在dp的时候,我们需要记录每个节点出发产生的最长路径和次长路径,以及他们的数量,数量的统计也是非常麻烦
详细请见代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mod 998244353
#define MAX 100000000
using namespace std;
int t,n,m,p,k,tt,f;
int x;
int head[10010];
typedef struct Node
{
int en;
int value;
int next;
}node;
node edge[20010];
typedef struct DPnode
{
int dp1,dp2,len,nn;
int n1,n2;
}DP;
DP dp[10010];
void ini()
{
int x,y,z;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d%d",&x,&y,&k);
edge[2*i-1].en=y;
edge[2*i-1].next=head[x];
edge[2*i-1].value=k;
head[x]=2*i-1;
edge[2*i].en=x;
edge[2*i].next=head[y];
edge[2*i].value=k;
head[y]=2*i;
}
}
void dfs(int s,int p)
{
dp[s].dp1=dp[s].dp2=dp[s].len=dp[s].n1=dp[s].n2=dp[s].nn=0;
int leaf=1;
for(int i=head[s];i;i=edge[i].next)
{
int q=edge[i].en;
if(q==p)
continue;
leaf=0;
dfs(q,s);
int tmp=dp[q].dp1+edge[i].value;
if(tmp>dp[s].dp1)
{
dp[s].dp2=dp[s].dp1;
dp[s].n2=dp[s].n1;
dp[s].dp1=tmp;
dp[s].n1=dp[q].n1;
}
else if(tmp==dp[s].dp1)
{
dp[s].n1+=dp[q].n1;
}
else if(tmp>dp[s].dp2)
{
dp[s].dp2=tmp;
dp[s].n2=dp[q].n1;
}
else if(tmp==dp[s].dp2)
{
dp[s].n2+=dp[q].n1;
}
}
if(leaf)
{
dp[s].n1=1;dp[s].nn=1;
dp[s].len=0;
dp[s].dp1=0;
return;
}
int c1=0,c2=0;
for(int i=head[s];i;i=edge[i].next)
{
int q=edge[i].en;
if(q==p)
continue;
int tmp=dp[q].dp1+edge[i].value;
if(tmp==dp[s].dp1)
c1++;
else if(tmp==dp[s].dp2&&dp[s].dp2)
c2++;
}
if(c1>1)
{
dp[s].len=dp[s].dp1*2;
int sum=0;
for(int i=head[s];i;i=edge[i].next)
{
int q=edge[i].en;
if(q==p)
continue;
if(dp[q].dp1+edge[i].value==dp[s].dp1)
{
dp[s].nn+=sum*dp[q].n1;
sum+=dp[q].n1;
}
}
}
else if(c2>0)
{
dp[s].len=dp[s].dp1+dp[s].dp2;
for(int i=head[s];i;i=edge[i].next)
{
int q=edge[i].en;
if(q==p)
continue;
if(dp[q].dp1+edge[i].value==dp[s].dp2)
{
dp[s].nn+=dp[s].n1*dp[q].n1;
}
}
}
else
{
dp[s].len=dp[s].dp1;
dp[s].nn=dp[s].n1;
}
return ;
}
void solve()
{
int ans=0;
int num=0;
for(int i=1;i<=n;i++)
{
if(dp[i].len>ans)
{
ans=dp[i].len;
num=dp[i].nn;
}
else if(dp[i].len==ans)
{
num+=dp[i].nn;
}
}
printf("%d %d\n",ans,num);
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(head,0,sizeof(head));
ini();
dfs(1,0);
solve();
}
return 0;
}
思路:最长边以前做过,用树形DP可以。求最长边的时候可以统计一下最长边的个数,那么以u为根节点的子树中最长边为:Len[u]+max(len[v]),个数node[u]*node[v]。
#include
#include
const int N=500000;
const int inf=0x3fffffff;
int head[N],num,Len[N],ans,node[N],k;
struct edge
{
int ed,w,next;
}e[N*2];
void addedge(int x,int y,int w)
{
e[num].ed=y;e[num].w=w;e[num].next=head[x];head[x]=num++;
e[num].ed=x;e[num].w=w;e[num].next=head[y];head[y]=num++;
}
void dfs(int u,int fa)
{
int i,v,temp;
Len[u]=0;//最长边
node[u]=1;//最长边的个数
for(i=head[u];i!=-1;i=e[i].next)
{
v=e[i].ed;
if(v==fa)continue;
dfs(v,u);
temp=Len[v]+e[i].w;
if(temp+Len[u]>ans)//最长边经过v
{
k=node[v]*node[u];
ans=temp+Len[u];
}
else if(temp+Len[u]==ans)
k+=node[v]*node[u];
if(Len[u]