首页 > 编程语言 >一个需要感性理解的树上算法 学习心得

一个需要感性理解的树上算法 学习心得

时间:2023-10-13 14:46:57浏览次数:46  
标签:rev return int siz ll 学习心得 算法 感性 void

题目描述

你现在有一颗 \(n\) 个点的树和 \(m\) 条由 \(x_i\) 到 \(y_i\) ( \(1 \le x_i\ ,\ y_i \le n\) ) 的简单可重复路径。求有多少种方案选路径,使路径集的大小为 \(k\) ,且所有路径至少有一个公共点。对 \(10^9+7\) 取模。

题解

这道题需要感性理解做法。我们记一个路径左右端点的最近公共祖先为这个路径的 “最高点” ( lca ),那么对于任意一个点而言,若有 \(x\) 条路径经过,其中 \(y\) 条的最高点不是这个点,那么这个点就能贡献 \(C_{x}^{x+y}-C_x^{y}\) 种方案。
感性理解,就是两个路径的交点肯定是其中一条的 lca ,而有 \(C_{x}^y\) 种方案会在更高的点上被统计,所以要减去。

原题链接 - Desire(desire) - Super

/*  
  bug:1.初始化fac的范围不是1~n而是1~N 除非在输入n之后初始化  
      2.树剖的rev 不是 rev[++dfc]=x 而是 rev[x] = ++dfc.  
 */  
#include<bits/stdc++.h>  
using namespace std;  
#define N 2000005  
#define ll long long  
#define P 1000000007ll  
int thr[N],hig[N],cf[N];  
int n,m,k;  
int tot,first[N],nxt[N*2],to[N*2];  
int top[N],rev[N],dep[N],siz[N],fah[N],son[N],dfc;  
ll fac[N];  
ll ans;  
void add(int x,int y){  
    nxt[++tot]=first[x];  
    first[x]=tot;  
    to[tot]=y;  
    return;  
}  
ll qpow(ll a,ll b){  
    ll c=1;  
    while(b>0){  
        if(b&1)c=c*a%P;  
        b>>=1;  
        a=a*a%P;  
    }  
    return c;  
}  
ll inv(ll x){  
    return qpow(x,P-2);  
}  
ll C(ll n,ll m){  
    if(n<m)return 0;  
    return fac[n]*inv(fac[m])%P*inv(fac[n-m])%P;  
}  
template<typename A,typename B>  
void Add(A &x,B y){  
    x+=y;  
    if(x>=P)x-=P;  
    return;  
}  
void spread(int x){  
    siz[x]=1;  
    for(int e=first[x];e;e=nxt[e]){  
        int u=to[e];  
        if(siz[u])continue;  
        dep[u]=dep[x]+1;  
        fah[u]=x;  
        spread(u);  
        siz[x]+=siz[u];  
        if(siz[u]>siz[son[x]])son[x]=u;  
    }  
    return;  
}  
void chain(int x,int curtop){  
    rev[x]=++dfc;  
    top[x]=curtop;  
    if(!son[x])return;  
    chain(son[x],curtop);  
    for(int e=first[x];e;e=nxt[e]){  
        int u=to[e];  
        if(top[u])continue;  
        chain(u,u);  
    }  
    return;  
}  
int ancestor(int x,int y){  
    while(top[x]!=top[y]){  
        if(dep[top[x]]<dep[top[y]])y=fah[top[y]];  
        else x=fah[top[x]];  
    }  
    if(rev[x]>rev[y])return y;  
    return x;  
}  
void getans(int x,int fa){  
    thr[x]=cf[x];  
    for(int e=first[x];e;e=nxt[e]){  
        int u=to[e];  
        if(u==fa)continue;  
        getans(u,x);  
        Add(thr[x],thr[u]);  
    }  
    if(hig[x]==0)return;  
    Add(ans,(C(thr[x],k)-C(thr[x]-hig[x],k)+P)%P);  
    return;  
}  
void read(int &x){  
    char ch=getchar();  
    x=0;  
    while(!isdigit(ch))ch=getchar();  
    while(isdigit(ch)){  
        x=x*10+ch-'0';  
        ch=getchar();  
    }  
    return;  
}  
template<typename T,typename...Types>  
void read(T &x,Types &...args){  
    read(x);  
    read(args...);  
    return;  
}  
int main(){  
    freopen("desire.in","r",stdin);  
    freopen("desire.out","w",stdout);  
    int x,y;  
    fac[0]=dep[1]=1;  
    for(int i=1;i<N;++i){  
        fac[i]=fac[i-1]*i%P;  
    }  
    read(n,m,k);  
    for(int i=1;i<n;++i){  
        read(x,y);  
        add(x,y);  
        add(y,x);  
    }  
    spread(1);  
    chain(1,1);  
    for(int i=1;i<=m;++i){  
        read(x,y);  
        int lca=ancestor(x,y);  
        ++cf[x];  
        ++cf[y];  
        --cf[lca];  
        --cf[fah[lca]];  
        ++hig[lca];  
    }  
    getans(1,0);  
    printf("%lld",ans);  
    return 0;  
}

标签:rev,return,int,siz,ll,学习心得,算法,感性,void
From: https://www.cnblogs.com/DZhearMins/p/17762051.html

相关文章

  • 网络流 - 最大流 学习心得
    一篇写的很好的博客那篇博客讲得很清楚,就不再赘述了。在这里贴出一些我犯过的bug:/*  bug:1.是q.front()而不是q.back()      2.q需要pop()      3.bfs的条件不是w!=0而是w>0      4.flow不会在同一层被更新,因此不能给flow赋值     ......
  • 树链剖分 学习心得
    Bug都写在代码开头了,就不复述了。还有一个智障的错误是注释调试代码的时候把同一行的正式代码也给注释掉了(写得非常精彩。/*  bug:1.rev、id要分清!      2.mod()函数的情况不能写一半就跑路!      3.别忘了先给tree build()一下!      4.出界......
  • 数位 dp 学习心得
    感觉非常牛逼。最牛逼的是很多情况下要去掉前导零。去掉前导零的方法通常是先忽略前导零的约束,最后再容斥掉有多少0。LuoguP2602数字计数来自【详细解释】数字计数ZJOJp2602一道练习数位DP的好题-moye到碗里来的博客-洛谷博客(luogu.com.cn)那么我们首先看题,对于这......
  • 割边+割点 学习心得
    先背诵tarjan板子#include<bits/stdc++.h>using namespace std;#define N 10005#define M 100005int tot,first[N],nxt[M],to[M];void add(int x,int y){    nxt[++tot]=first[x];    first[x]=tot;    to[tot]=y;}int n......
  • 互补滤波姿态解算算法思路
    ......
  • Lnton羚通机器视觉算法平台不系安全带违法智能监控解决方案
    Lnton羚通的算法算力云平台是一款优秀的解决方案,具有突出的特点。它提供高性能、高可靠性、高可扩展性和低成本的特性,使用户能够高效地执行复杂计算任务。此外,平台还提供丰富的算法库和工具,并支持用户上传和部署自定义算法,提升了平台的灵活性和个性化能力。在发生交通事故时,很多事......
  • 【离线算法】- 莫队
    莫队简介莫队是可以支持多次询问区间\([l,r]\)的信息的离线算法。通过将询问范围以块长为\(\sqrtn\)分块后按端点所属分块排序的方式优化复杂度。普通莫队定义普通莫队针对的是序列上的区间询问。常见形式为:对于一个长度为\(n\)的序列,提出\(m\)次询问,每次询问区间......
  • 10.13算法
    你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非负整数数组,计算你不触动警报装置的情况下,一夜之内能......
  • 算法学习笔记(30):Kruskal 重构树
    Kruskal重构树这是一种用于处理与最大/最小边权相关的一个数据结构。其与kruskal做最小生成树的过程是类似的,我们考虑其过程:按边权排序,利用并查集维护连通性,进行合并。如果我们在合并时,新建一个节点,其权值为当前处理的边的权值,并将合并的两个节点都连向新建的节点,那么就......
  • 算法训练day30 LeetCode93.78.90
    算法训练day30LeetCode93.78.9093.复原IP地址题目93.复原IP地址-力扣(LeetCode)题解代码随想录(programmercarl.com)使用'.'切割字符串、结束条件为字符串中有三个'.'、同时要确定字符串符合的条件长度为不为1时,首字符不能是0数值大小在[0,255]单个字符在'0'......