1 条题解
-
0
简要题意:给定一张 个点 条边的简单无向连通图,定义一条路径合法当且仅当不连续经过同一条边,求从点 到点 的经过 条边的合法路径数。
首先在不考虑图的特殊性质的情况下,有一个很明显的朴素的 DP 做法:将所有无向边拆成两条有向边并标号,定义 为满足经过 条边且最后经过的有向边是第 条边的合法路径数,答案即为满足第 条边的终点为 的数字 的 之和,转移时可暴力枚举所有边,时间复杂度为 。
然后注意到题目保证图连通且 ,这意味着图一定有一棵生成树,且在找到生成树后图中的非树边只有 条,在该题中该值小于 。
设 ,考虑在朴素 DP 算法上加以修改:先在图中找到一棵生成树,再将剩余的将 条非树边拆成两条有向的非树边并标号。定义 为满足经过 条边且最后经过的有向的非树边是第 条边的合法路径数, 为第 条有向的非树边的起点, 为第 条有向的非树边的终点, 为点 与点 在树上的唯一路径的边数,则可以通过以下步骤计算 :
-
若 ,则初始令 为 ,否则令 为 。
-
枚举所有满足 且并非从同一条非树边拆出来的有向的非树边 ,令 $dp_{i,j}\to dp_{i,j}+dp_{i-\operatorname{dis}(r_x,l_j)-1,x}$。
由于在一棵树上任意两点间只有一条合法路径,因而以上步骤的正确性显然。而统计答案 也可使用类似的步骤:
-
若 ,则初始令 为 ,否则令 为 。
-
枚举所有满足 的有向的非树边 ,令 。
注意到上述全部过程中只会有 个不同的 被用到,因而可以使用倍增法求 LCA 相关算法以 的时间复杂度预处理出来;而这样做则 DP 及统计答案的过程的时间复杂度合起来为 。同时其它过程(如求一棵生成树)的时间复杂度均不大于上面两个中的至少一个,因而该算法的时间复杂度为 ,在本题 ,, 的特殊数据范围下可以通过。
以下为代码,为方便实现,代码实际执行流程与上述做法做法在细节上有微小差别,但大致做法仍相同,且不影响正确性与时空复杂度:
#include<bits/stdc++.h> using namespace std; long long n,m,k,u,v,st[200001],si[200001],len,l[23],r[23],dp[10001][23],fa[200001][21],dep[200001],di[23][23],ans; const int mod=1e9+7; vector<int>tr[200001]; int find(int p) { if(st[p]==p)return p; return st[p]=find(st[p]); } int unio(int p,int q) { p=find(p);q=find(q); if(p==q)return 0; if(si[p]<si[q])swap(p,q); st[q]=p; si[p]+=si[q]; return 1; } void dfs(int p) { for(int i=0;i<tr[p].size();i++) { if(tr[p][i]!=fa[p][0]) { fa[tr[p][i]][0]=p; dep[tr[p][i]]=dep[p]+1; dfs(tr[p][i]); } } } int lca(int p,int q) { if(dep[p]<dep[q])swap(p,q); for(int i=20;i>=0;i--) { if(dep[fa[p][i]]>=dep[q]) { p=fa[p][i]; } } if(p==q)return p; for(int i=20;i>=0;i--) { if(fa[p][i]!=fa[q][i]) { p=fa[p][i]; q=fa[q][i]; } } return fa[p][0]; } int dis(int p,int q) { return dep[p]+dep[q]-2*dep[lca(p,q)]; } int main() { ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin>>n>>m>>k; for(int i=1;i<=n;i++) { st[i]=i; si[i]=1; } for(int i=1;i<=m;i++) { cin>>u>>v; if(unio(u,v)) { tr[u].push_back(v); tr[v].push_back(u); } else { l[++len]=u; r[len]=v; l[++len]=v; r[len]=u; } } dep[1]=1;dfs(1); for(int i=1;i<=20;i++) { for(int j=1;j<=n;j++) { fa[j][i]=fa[fa[j][i-1]][i-1]; } } for(int i=1;i<=len;i++) { di[0][i]=dis(1,l[i]); for(int j=1;j<=len;j++) { di[i][j]=dis(r[i],l[j]); } } for(int i=1;i<=k;i++) { for(int j=1;j<=len;j++) { if(di[0][j]==i-1) { dp[i][j]=1; } for(int x=1;x<=len;x++) { if(di[x][j]<i&&(j-1^1)!=x-1) { dp[i][j]+=dp[i-di[x][j]-1][x]; dp[i][j]%=mod; } } } } if(dis(1,n)==k)ans=1; for(int i=1;i<=len;i++) { if(dis(r[i],n)<k) { ans+=dp[k-dis(r[i],n)][i]; ans%=mod; } } cout<<ans; } -
- 1
信息
- ID
- 12558
- 时间
- 1000ms
- 内存
- 700MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者