zoukankan      html  css  js  c++  java
  • 花花的森林

    问题描述

    花花有一棵带n个顶点的树T,每个节点有一个点权ai
    有一天,他认为拥有两棵树更好一些。所以,他从T中删去了一条边。
    第二天,他认为三棵树或许又更好一些。因此,他又从他拥有的某一棵树中去除了一条边。
    如此往复。每一天,花花都会删去一条尚未被删去的边,直到他得到了一个包含了n棵只有一个点的树的森林。
    定义一条简单路径的权值为路径上点权之和,一棵树的直径为树上权值最大的简单路径。花花认为树最重要的特征就是它的直径。所以他想请你算出任一时刻他拥有的所有树的直径的乘积。因为这个数可能很大,他要求你输出乘积对109+7取模之后的结果

    输入格式

    输出格式

    样例输入

    样例输出

    提示

    数据范围

    题解

    把删边变成加边,倒过来做,维护直径,就变成了初始有n个集合,每个集合只有一个点,每次加一条边,合并两个集合,再求生成新树的直径

    两棵树合并后的直径,它的端点一定从这两棵树原本的直径中取

    预处理好最终树的形态,利用LCA和并查集

    每次加边,更新直径的时候,要把答案除掉原来的直径的权值,而取模运算的规律对除法不适用,要用逆元转成乘法

     1 #include <cstdio>
     2 const int maxn=1e9+7;
     3 struct node{
     4     int u,nex;
     5 }g[200005];
     6 struct note{
     7     int u,v;
     8     note(int u=0,int v=0):u(u),v(v){ }
     9 }e[100005],p[100005];
    10 int n,a[100005],fir[100005],num,fa[100005],dep[100005],d[100005];
    11 int ans[100005],b[100005],dia[100005],f[100005][20],xx,yy,maxd;
    12 void add(int x,int y)
    13 {
    14     g[++num].u=y;  g[num].nex=fir[x];  fir[x]=num;
    15     return;
    16 }
    17 int ksm(int a,int b)
    18 {
    19     int s=1;
    20     while (b)
    21     {
    22         if (b&1) s=1ll*s*a%maxn;
    23         a=1ll*a*a%maxn;
    24         b>>=1;
    25     }
    26     return s;
    27 }
    28 int find(int x)
    29 {
    30     if (x==fa[x]) return x;
    31     return fa[x]=find(fa[x]);
    32 }
    33 void dfs(int x,int fa)
    34 {
    35     int i,k;
    36     dep[x]=dep[fa]+1;  d[x]=d[fa]+a[x];  f[x][0]=fa;
    37     for (i=1;i<=17;i++) 
    38       f[x][i]=f[f[x][i-1]][i-1];
    39     for (k=fir[x];k;k=g[k].nex)
    40       if (g[k].u!=fa)
    41         dfs(g[k].u,x);
    42     return;
    43 }
    44 int lca(int x,int y)
    45 {
    46     int i;
    47     if (dep[y]<dep[x]) x^=y,y^=x,x^=y;  
    48     for (i=17;i>=0;i--)
    49       if (dep[f[y][i]]>=dep[x])
    50         y=f[y][i];
    51     if (x==y) return x;
    52     for (i=17;i>=0;i--)
    53       if (f[y][i]!=f[x][i])
    54         y=f[y][i],x=f[x][i];
    55     if (x==y) return x;
    56     return f[x][0];
    57 }
    58 void dis(int x,int y)
    59 {
    60     int par=lca(x,y),s;
    61     s=d[x]+d[y]-d[par]*2+a[par];
    62     if (s>maxd) maxd=s,xx=x,yy=y;
    63     return;
    64 }
    65 int main()
    66 {
    67     freopen("a.in","r",stdin);
    68     int i,j,k,x,y,fu,fv;
    69     scanf("%d",&n);
    70     for (ans[n]=i=1;i<=n;i++) 
    71       scanf("%d",&a[i]),
    72       ans[n]=1ll*ans[n]*a[i]%maxn,
    73       dia[i]=a[i],fa[i]=i,p[i]=note(i,i); 
    74     for (i=1;i<n;i++)
    75       scanf("%d%d",&x,&y),
    76       add(x,y),add(y,x),
    77       e[i]=note(x,y);
    78     for (i=1;i<n;i++) 
    79       scanf("%d",&b[i]);
    80     d[1]=a[1];  dfs(1,0);
    81     for (i=n-1;i>=1;i--)
    82     {
    83         fu=find(e[b[i]].u);  fv=find(e[b[i]].v);
    84         ans[i]=1ll*ans[i+1]*ksm(dia[fu],maxn-2)%maxn*1ll*ksm(dia[fv],maxn-2)%maxn;
    85         fa[fv]=fu;  maxd=0;
    86         dis(p[fu].u,p[fv].u);  dis(p[fu].v,p[fv].v);
    87         dis(p[fu].u,p[fv].v);  dis(p[fu].v,p[fv].u);
    88         if (dia[fu]>maxd) maxd=dia[fu],xx=p[fu].u,yy=p[fu].v;
    89         if (dia[fv]>maxd) maxd=dia[fv],xx=p[fv].u,yy=p[fv].v;
    90         dia[fu]=maxd;  p[fu]=note(xx,yy);
    91         ans[i]=1ll*ans[i]*maxd%maxn;
    92     }
    93     for (i=1;i<=n;i++)
    94       printf("%d
    ",ans[i]);
    95     return 0;
    96 }
  • 相关阅读:
    【LINUX编程】一个基于C/S结构的简单通讯程序
    【LINUX内核】LINUX内核编译
    C语言中的auto, static, const, extern, register, restrict, volatile 关键字
    【LINUX编程】Makefile的基本介绍
    【LINUX编程】关于man的详细用法
    DOTween动画插件详解
    cas server端的loginwebflow详细流程
    Linux SSH远程文件/目录传输命令scp
    C#去掉字符串中特定ASC码字符
    解決 Flash 蓋住彈出目錄的方法(转载)
  • 原文地址:https://www.cnblogs.com/rabbit1103/p/9764697.html
Copyright © 2011-2022 走看看