1 条题解

  • 0
    @ 2026-8-6 22:59:23

    简要题意:给定一张 nn 个点 mm 条边的简单无向连通图,定义一条路径合法当且仅当不连续经过同一条边,求从点 11 到点 nn 的经过 kk 条边的合法路径数。

    首先在不考虑图的特殊性质的情况下,有一个很明显的朴素的 DP 做法:将所有无向边拆成两条有向边并标号,定义 dpi,jdp_{i,j} 为满足经过 ii 条边且最后经过的有向边是第 jj 条边的合法路径数,答案即为满足第 jj 条边的终点为 nn 的数字 jjdpk,jdp_{k,j} 之和,转移时可暴力枚举所有边,时间复杂度为 O(km2)O(km^2)

    然后注意到题目保证图连通且 mn+10m\le n+10,这意味着图一定有一棵生成树,且在找到生成树后图中的非树边只有 mn+1m-n+1 条,在该题中该值小于 1111

    d=mn+1d=m-n+1,考虑在朴素 DP 算法上加以修改:先在图中找到一棵生成树,再将剩余的将 dd 条非树边拆成两条有向的非树边并标号。定义 dpi,jdp_{i,j} 为满足经过 ii 条边且最后经过的有向的非树边是第 jj 条边的合法路径数, lil_i 为第 ii 条有向的非树边的起点, rir_i 为第 ii 条有向的非树边的终点, dis(i,j)\operatorname{dis}(i,j) 为点 ii 与点 jj 在树上的唯一路径的边数,则可以通过以下步骤计算 dpi,jdp_{i,j}

    1. dis(1,j)=i1\operatorname{dis}(1,j)=i-1,则初始令 dpi,jdp_{i,j}11,否则令 dpi,jdp_{i,j}00

    2. 枚举所有满足 dis(rx,lj)<i\operatorname{dis}(r_x,l_j)<i 且并非从同一条非树边拆出来的有向的非树边 xx,令 $dp_{i,j}\to dp_{i,j}+dp_{i-\operatorname{dis}(r_x,l_j)-1,x}$。

    由于在一棵树上任意两点间只有一条合法路径,因而以上步骤的正确性显然。而统计答案 ansans 也可使用类似的步骤:

    1. dis(1,n)=k\operatorname{dis}(1,n)=k,则初始令 ansans11,否则令 ansans00

    2. 枚举所有满足 dis(ri,n)<k\operatorname{dis}(r_i,n)<k 的有向的非树边 ii,令 ansans+dpidis(ri,n),ians\to ans+dp_{i-\operatorname{dis}(r_i,n),i}

    注意到上述全部过程中只会有 O(d2)O(d^2) 个不同的 dis(i,j)\operatorname{dis}(i,j) 被用到,因而可以使用倍增法求 LCA 相关算法以 O(nlogn+d2logn)O(n\log n+d^2\log n) 的时间复杂度预处理出来;而这样做则 DP 及统计答案的过程的时间复杂度合起来为 O(kd2)O(kd^2)。同时其它过程(如求一棵生成树)的时间复杂度均不大于上面两个中的至少一个,因而该算法的时间复杂度为 O(nlogn+d2logn+kd2)O(n\log n+d^2\log n+kd^2),在本题 n2105n\le 2\cdot 10^5d11d\le 11k104k\le 10^4 的特殊数据范围下可以通过。

    以下为代码,为方便实现,代码实际执行流程与上述做法做法在细节上有微小差别,但大致做法仍相同,且不影响正确性与时空复杂度:

    #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
    上传者