树形DP 进阶题

emmmm我之前那篇博客都是一些普及提高的树形DP水题,基本都是一个模板能够解决的问题。现在让我们来进阶一下。
**

T1

**

HOT-Hotels

传送门

有一个树形结构,每条边的长度相同,任意两个节点可以相互到达。选3个点。两两距离相等。有多少种方案?

难度:省选+/NOI-
这道题的难度并不是不能接受,一开始会觉得很难但是仔细想过之后会发现其实还好。

首先,我们要求任意三个点互相到达的距离相等,同时它是一个树形结构
所以,仔细想一下可以得知,如果要让三个点互相距离相同,那么只会有一种方案,也就是,一个中心店,即他们到这个中心点距离相同,然后它们之间互相的距离也是一样的。
如果一时不能理解的话,请想过后再往下看。

那么,这个这个中心点就成为了我们做这道题的入手。
我们可以枚举这个树形结构的每一个点作为根,那么,在我们每次枚举出的这棵树里,每一层的节点是不是就是高度相同?
也就是,这些点每三个就可以作为一个方案,
然后我们的任务就是来求方案了
根据小学乘法原理,
譬如这个根有三个子树,在这一层 的节点数分别是a,b,c,那么方案数是不是就是a*b*c?
同理如果这个根再多一个子树那么方案数是不是还要多
abd+acd+bcd a ∗ b ∗ d + a ∗ c ∗ d + b ∗ c ∗ d
(ab+ac+bc)d ( a ∗ b + a ∗ c + b ∗ c ) ∗ d
那么再多一颗子树也依然如此。

所以这就是我们的突破口了,
用一个sum2数组维护a*b+b*c+a*c………等这一式子;每次新得到的节点数为K
那么新增加的答案数为sum2*K;
设sum1为当前这一层当前子树之前的所有节点之和,
设tot[i]为当前子树第i层的节点数
那么框架就是
1.枚举根节点k
2.枚举每一棵子树,计算第i层的tot[i];
3.计算部分:
ans=ans+tot[i]sum2[i]; a n s = a n s + t o t [ i ] ∗ s u m 2 [ i ] ;
sum2[i]=sum2[i]+sum1[i]tot[i] s u m 2 [ i ] = s u m 2 [ i ] + s u m 1 [ i ] ∗ t o t [ i ]
sum1[i]=sum1[i]+tot[i] s u m 1 [ i ] = s u m 1 [ i ] + t o t [ i ]
tot[i]=0// t o t [ i ] = 0 / / 清 零 ∗ ∗

然后就是每次枚举树根之后,sum1sum2要清零。

代码:

#include
using namespace std;

const int maxn=5010;
int n,m,cnt,head[maxn],sum1[maxn],sum2[maxn],dep,tot[maxn];
long long ans;
inline int read()
{
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}

struct node
{
    int v,nxt;
}e[maxn<<1];

void add(int u,int v)
{
    cnt++;
    e[cnt].nxt=head[u];
    e[cnt].v=v;
    head[u]=cnt;
}
void getfun(int v,int u,int deep)
{
    dep=max(dep,deep);
    tot[deep]++;
    for (int i=head[v]; i; i=e[i].nxt)
      {
        int y=e[i].v; 
        if (y==u) continue;
        getfun(y,v,deep+1);
      }
}

int main()
{
    n=read();
    for (int i=1; iint x,y;
        x=read(),y=read();
        add(x,y);
        add(y,x);
      }
    for (int i=1; i<=n; i++)
      {
        memset(sum1,0,sizeof(sum1));
        memset(sum2,0,sizeof(sum2));
        for (int j=head[i]; j; j=e[j].nxt)
          {
            int v=e[j].v;
            dep=0;
            getfun(v,i,1);
            for(int k=1;k<=dep;k++)
              {
               ans+=tot[k]*sum2[k];
               sum2[k]=sum2[k]+sum1[k]*tot[k];
               sum1[k]=sum1[k]+tot[k];
               tot[k]=0;
              }
          }
      }
    cout<

大佬解法好
感谢大佬

你可能感兴趣的:(新知识,个人观点)