HDU 4705 Y (树形DP+计数)*

Y

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 2865    Accepted Submission(s): 835


 

Problem Description

 

Sample Input

 

4 1 2 1 3 1 4

 

Sample Output

 

1

Hint

1. The only set is {2,3,4}. 2. Please use #pragma comment(linker, "/STACK:16777216")

 

Source

2013 Multi-University Training Contest 10

 

Recommend

zhuyuanchen520   |   We have carefully selected several similar problems for you:  6437 6436 6435 6434 6433 

#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define read(x,y) scanf("%d%d",&x,&y)

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ll long long
ll gcd(ll x,ll y)  {  return y==0?x:gcd(y,x%y); }
const int  maxn =1e5+5;
const int mod=1e9+7;
/*
题目大意:找出树中三点对对数,
满足一条简单路径不覆盖这三点对。

这道题明显的树形DP计数的味道在里面,
正难则反,我在思考找符合题意的三点对时也是踩了不少坑,
主要打破我幻想的一个例子就是,可以存在两个点在一个子树中, 
另一个点在子树外面的情况,这样我的想法就有漏洞。

正难则反,我们考虑一条路径能覆盖的三点对,a,b,c,
在遍历根节点时,假设一个节点在根节点上,另一个点在当前枚举的子树中,
那么如何完全不重复的统计这类情况的个数呢?
在更新sons[u]的过程中,sons[u]同时也是子树节点数量的前缀和,
所以用总结点数减去该前缀和就得到未和当前子树配对的节点数量,
注意这里并没有重复计算,因为之前计算过的肯定不会与当前节点配对,
在总数中已经减去了。


*/

int n,x,y;
///链式前向星
struct node
{
    int u,nxt;
};
node edge[maxn<<2];
int head[maxn],tot=0;
void init()
{
    memset(head,-1,sizeof(head));
    tot=0;
}
void add(int x,int y)
{
    edge[tot]=node{y,head[x]};
    head[x]=tot++;
}

ll ans,sons[maxn];///计数

void dfs(int u,int pre,int dis)
{
    sons[u]=1;
    for(int i=head[u];~i;i=edge[i].nxt)
    {
        int v=edge[i].u;
        if(v==pre) continue;

        dfs(v,u,dis+1);

        sons[u]+=sons[v];
        ans+=1LL*(n-sons[u])*sons[v];
    }
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        init();
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }

        ans=0;
        memset(sons,0,sizeof(sons));

        dfs(1,-1,0);

        ll tot=1LL*n*(n-1)*(n-2)/6;

        printf("%lld\n",tot-ans);
    }
    return 0;
}
/*
6
1 2
1 3
3 4
3 5
1 6

4 6
4 7
5 8
5 9


*/

猜你喜欢

转载自blog.csdn.net/qq_37451344/article/details/81980582
今日推荐