zoukankan      html  css  js  c++  java
  • 2018 xuzhou icpc G. Rikka with Intersections of Paths

    链接  

    题意: 

    给定一棵n个节点的树  有m条路径  问有多少种方案 使得 从m条路径里面选k条路径  这k条路径至少包含一个公共点

    题解:

    • 首先可以用树上差分求出  有多少条路径经过某个点
    • 但是不能直接用该信息来统计答案  因为这k条路径可能包含多个公共点  会重复统计答案
    • 为了避免多次重复统计   可以转化到lca上  利用lca的唯一性
    • 所以当某个点为某条边 或几条边 的lca时 统计这条边的答案集  
    • 该统计方案一定不会重复 可以随便模拟试试
    #include<bits/stdc++.h>
    using namespace std;
    const int N=3e5+6;
    const int mod=1e9+7;
    #define ll long long
    int head[N],pos,sum[N],n,m,k,fa[N][20],dep[N],cnt[N];
    ll inv[N],fac[N];
    struct Edge{int to,nex;}edge[N<<1];
    void add(int a,int b){edge[++pos]=(Edge){b,head[a]};head[a]=pos;}
    
    void dfs(int x,int f)
    {
        fa[x][0]=f;dep[x]=dep[f]+1;
        for(int i=head[x];i;i=edge[i].nex)
        {
            int v=edge[i].to;if(v==f)continue;
            dfs(v,x);
        }
    }
    int getlca(int x,int y)
    {
        if(dep[x]<dep[y])swap(x,y);
        for(int i=19; i>=0; --i) {
            if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
        }
        if(x==y) return x;
        for(int i=19; i>=0; --i) {
            if(fa[x][i]!=fa[y][i]) x=fa[x][i], y=fa[y][i];
        }
        return fa[x][0];
    }
    
    void dfs2(int x,int fa)
    {
        for(int i=head[x];i;i=edge[i].nex)
        {
            int v=edge[i].to;
            if(v==fa)continue;
            dfs2(v,x);sum[x]+=sum[v];
        }
    }
    ll C(int n,int m)
    {
        if(n<m||m==0)return 0;
        return fac[n]*inv[m]%mod*inv[n-m]%mod;
    }
    ll calc(int x)
    {
        ll ans=C(sum[x],k);
        ans=(ans-C(sum[x]-cnt[x],k)%mod)%mod;
        return (ans+mod)%mod;
    }
    int main()
    {      
        inv[0]=inv[1]=fac[1]=1;
        for(int i=2;i<=N;i++)fac[i]=fac[i-1]*i%mod;
        for(int i=2;i<=N;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        for(int i=2;i<=N;i++)inv[i]=inv[i-1]*inv[i]%mod;
        int cas;cin>>cas;
    
        while(cas--)
        {
            cin>>n>>m>>k;int u,v;
            for(int i=1;i<n;i++)scanf("%d%d",&u,&v),add(u,v),add(v,u);
            dfs(1,0);
    
            for(int j=1;j<20;j++)
                for(int i=1;i<=n;i++)fa[i][j]=fa[fa[i][j-1]][j-1];
            while(m--)
            {   
                scanf("%d%d",&u,&v);
                int lca=getlca(u,v);
                sum[u]++;sum[v]++;
                sum[lca]--;sum[fa[lca][0]]--;
                cnt[lca]++;
            }
            dfs2(1,0);
            ll ans=0;
            for(int i=1;i<=n;i++)ans+=calc(i),ans%=mod;
            cout<<ans<<endl;
            for(int i=1;i<=n;i++)sum[i]=head[i]=dep[i]=cnt[i]=0;pos=0;
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    [No000088]并行循环vs普通循环
    [No000087]Linq排序,SortedList排序,二分法排序性能比较
    [No000086]C#foreach集合被改变,报错处理方案
    [No000085]C#反射Demo,通过类名(String)创建类实例,通过方法名(String)调用方法
    [No000084]C# 使用Log4Net(1)-快速建立一个demo
    [No000082]Convert和Parse的区别/Convert.ToInt32()与int.Parse()的区别
    [No000081]SVN学习笔记1-服务端搭建
    [No00007F]2016-面经[下] 英文简历写作技巧
    [No00007E]2016-面经[中]
    [No00007D]2016-面经[上]
  • 原文地址:https://www.cnblogs.com/bxd123/p/11707306.html
Copyright © 2011-2022 走看看