2018ICPC徐州现场赛G题

5 篇文章 0 订阅

G. Rikka with Intersections of Paths

time limit per test

6.0 s

memory limit per test

1024 MB

input

standard input

output

standard output

Rikka has a tree TT with nn vertices numbered from 11 to nn.

Meanwhile, Rikka has marked mm simple paths in TT, the ii-th of which is between the vertices xixi and yiyi, where some of them could be the same path.

Now, Rikka wants to know in how many different strategies she can select kk paths from the marked paths such that those selected paths share at least one common vertex.

Input

The input contains several test cases, and the first line contains a single integer TT (1≤T≤2001≤T≤200), the number of test cases.

For each test case, the first line contains three integers nn (1≤n≤3×1051≤n≤3×105), the size of the tree TT, mm (2≤m≤3×1052≤m≤3×105), the number of marked paths, and kk (2≤k≤m2≤k≤m).

The following (n−1)(n−1) lines describe the tree TT. Each of them contains two integers uu and vv (1≤u,v≤n1≤u,v≤n, u≠vu≠v), representing an edge between the vertices uu and vv.

The following mm lines describe all marked simple paths in the tree. The ii-th of them contains two integers xixi and yiyi (1≤xi,yi≤n1≤xi,yi≤n).

The input guarantees that the sum of nn and the sum of mm in all test cases are at most 2×1062×106 respectively.

Output

For each test case, output a single line with a single integer, the number of different strategies meeting the requirement modulo (109+7)(109+7).

Example

input

Copy

1
3 6 2
1 2
1 3
1 1
2 2
3 3
1 2
1 3
2 3

output

Copy

10

题意:给出一个n个的无向图,n-1条边。并给出m条被标记的边,问有多少种方案选k条被标记的路径使得它们至少有一个公共的顶点。

学过树上差分的话应该可以从题目的要求看出端倪这题用树上差分,至于为何是这样的组合数计算方式,to be hones,我也还没懂。

#include <bits/stdc++.h>
using namespace std;
const int N=300000+200;
typedef long long ll;
int n,m,ans;
#define mod 1000000007
ll fac[N]={1,1},inv[N]={1,1},f[N]={1,1};
struct LCA
{
    int head[N<<1],Next[N<<1],edge[N<<1],ver[N<<1],tot=0;
    int deep[N],fa[N][22],lg[N],date[N],date1[N];

    inline void init()
    {
        memset(head,0,sizeof(head));
        memset(deep,0,sizeof(deep));
        memset(fa,0,sizeof fa);
        memset(lg,0,sizeof lg);
        memset(date1,0,sizeof date1);
        memset(date,0,sizeof date);
        tot=0;
    }

    inline void add_edge(int a,int b,int c)
    {
        edge[++tot]=b;
        ver[tot]=a;
        Next[tot]=head[a];
        head[a]=tot;
    }

    inline void dfs(int x,int y)
    {
        deep[x]=deep[y]+1;
        fa[x][0]=y;
        for(int i=1; (1<<i)<=deep[x]; i++)
            fa[x][i]=fa[fa[x][i-1]][i-1];
        for(int i=head[x]; i; i=Next[i])
            if (edge[i]!=y)
                dfs(edge[i],x);
        return ;
    }

    inline int Lca(int x,int y)
    {
        if (deep[x]<deep[y])
            swap(x,y);
        while(deep[x]>deep[y])
            x=fa[x][lg[deep[x]-deep[y]]-1];
        if(x==y)
            return x;
        for(int i=lg[deep[x]]; i>=0; i--)
            if (fa[x][i]!=fa[y][i])
            {
                x=fa[x][i];
                y=fa[y][i];
            }
        return fa[x][0];
    }

    inline void query(int x,int f)
    {
        for(int i=head[x]; i; i=Next[i])
        {
            int j=edge[i];
            if (j!=f)
            {
                query(j,x);
                date[x]+=date[j];
                date1[x]+=date1[j];
            }
        }
    }

    inline void update(int x,int y)
    {
        int z=Lca(x,y);
        date[x]++;
        date[y]++;
        date[z]-=2;
        date1[x]++;
        date1[y]++;
        date1[z]--;
        date1[fa[z][0]]--;
    }

    inline ll C(int a,int b)
    {
        if (a<b) return 0;
        return fac[a]*inv[b]%mod*inv[a-b]%mod;
    }

    inline ll getans(int k)
    {
       ll ans=0;
       for(int i=1;i<=n;i++)
       {
           ans=(ans+C(date1[i],k)%mod)%mod;
       }
       for(int i=2;i<=n;i++)
       {
           ans=(ans-C(date[i],k)%mod+mod)%mod;
       }
       return (ans%mod);
    }
} g1;

void init()
{
    for(int i=2;i<300010;i++)
    {
        fac[i]=fac[i-1]*i%mod;
        f[i]=(mod-mod/i)*f[mod%i]%mod;
        inv[i]=inv[i-1]*f[i]%mod;
    }
}
int main()
{
    init();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int k;
        scanf("%d%d%d",&n,&m,&k);
        g1.init();
        for(int i=1; i<n; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            g1.add_edge(a,b,0);
            g1.add_edge(b,a,0);
        }
        for(int i=1; i<=n; i++)
            g1.lg[i]=g1.lg[i-1]+(1<<g1.lg[i-1]==i);
        g1.dfs(1,0);
        for(int i=1; i<=m; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            g1.update(a,b);
        }
        g1.query(1,0);
        printf("%lld\n",g1.getans(k));
    }

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值