4835: [Lydsy2017年4月月赛]遗忘之树

44 篇文章 0 订阅

4835: [Lydsy2017年4月月赛]遗忘之树

Time Limit: 3 Sec Memory Limit: 128 MB
Submit: 120 Solved: 53
[Submit][Status][Discuss]
Description

定义任意两点之间存在唯一路径的无向图是树。对于一棵n个点的树,如果删掉某个点u之后每个连通块的大小均不
超过n/2,那么称u为这棵树的重心。现在有一棵n个点的树T,利用过程P来构造一个n个点的有向图G,初始G没有边
。现在对T调用过程P,P的内容如下:
1:删去u,对每个连通块递归调用过程P;
2:对每个连通块,如果它的标号最小的重心为v,那么在图G中连一条u到v的有向边。
3:现在小Q同学手里有一个图G,但是不记得原来T的样子了,希望你能通过G来恢复T,但是可能得到的T会有很多种
你只需要告诉小Q同学可能的T的个数。
两棵树被认为是不同的,当且仅当存在一对点(u,v),使得u和v在一棵树中有边,在另一棵树中没有边。
Input

第一行是一个整数T(1≤T≤1000),表示测试数据的组数。

对于每组测试数据:
第一行是两个整数n和m(2≤n,m≤100000),表示G的点数的边数。
接下来m行,每行是两个整数u和v(1≤u,v≤n),表示有一条从u到v的有向边。
保证对于每组测试数据,至少存在一棵树T,使得对T调用过程P之后可以得到G
并且所有测试数据的n之和、m之和均不超过10^6。
Output

对于每组测试数据,输出一行一个非负整数,表示这组数据的答案对(10^9+7)取模的值。

Sample Input

2

3 2

1 2

1 3

4 3

1 3

2 1

2 4

Sample Output

1

1
HINT

Source

鸣谢Tangjz提供试题

定义状态 f[i] 为在重心树内以 i 为根的子树中合法方案数
那么对于每个儿子,可以用乘法原理乘起来
每个儿子只需考虑有多少点能和当前点直接连边
一个子树中的点不能和当前点连边,当且仅当连了以后重心改变
先特判会不会出现重心改变的情况
有多少编号更小的点可以暴力
因为一个点的深度只有O(logn)
总复杂度 O(nlogn)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;

const int maxn = 1E5 + 10;
typedef long long LL;
const LL mo = 1000000007;

int n,m,T,rt,f[maxn],nex[maxn],last[maxn],siz[maxn];
bool vis[maxn];

vector <int> v[maxn];

#define Mul(x,y) (1LL * (x) * (y) % mo)

inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}

inline void Dfs(int x)
{
    siz[x] = f[x] = 1; last[x] = x; nex[x] = 0;
    for (int i = 0; i < v[x].size(); i++)
    {
        int y = v[x][i]; Dfs(y); siz[x] += siz[y];
    }
    bool flag = !(siz[x] & 1); int tmp = siz[x] >> 1;
    for (int i = 0; i < v[x].size(); i++)
    {
        int y = v[x][i],k = 0;
        if (flag && siz[y] == tmp)
        {
            for (int g = y; g; g = nex[g])
                if (g > x) ++k;
        }
        else k = siz[y];
        f[x] = Mul(f[x],Mul(f[y],k));
        nex[last[x]] = y; last[x] = last[y];
    }
}

inline void Solve()
{
    n = getint(); m = getint();
    while (m--)
    {
        int x = getint(),y = getint();
        v[x].push_back(y); vis[y] = 1;
    }
    for (int i = 1; i <= n; i++) if (!vis[i]) {rt = i; break;}
    Dfs(rt); printf("%d\n",f[rt]);
}

inline void Clear()
{
    for (int i = 1; i <= n; i++)
        v[i].clear(),vis[i] = 0;
}

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    T = getint();
    while (T--) Solve(),Clear();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值