Counting Shortcuts(CF 1650 G)

传送门

题目大意

T组样例,每组样例有n个点,m条边(无重边,无自环),起点为s,终点为t,问起点到终点的路径(可以不是简单路径)中,最短路的条数加上次短路的条数,答案对1e9+7取模。(1T104,2n2105,1m2105,st)

思路

虽然题目说可以不是简单路径,但是很容易能发现,真正能计数的还是简单路径。所以我们就可以先预处理出第i个点到起点的最短距离dep[i],然后根据dep[i]从小到大对i进行排序,不知道叫啥名儿,就先暂且叫它bfs序吧。然后我们可以再来一遍bfs,这时候考虑dp转移。我们定义dp1[i]si的路径中最短路的条数,dp2[i]si的路径中次短路的条数,然后我们根据刚才处理出来的bfs序,按照bfs序的顺序,对于点u,遍历与他相邻的点v,如果dep[u]==dep[v],那就可以dp2[v]+=dp1[u],如果dep[u]+1==dep[v],就可以dp1[v]+=dp1[u],dp2[v]+=dp2[u]。但是这样还有一个问题,就是dp1[u]还没完全更新完你就在拿它更新dp2[v]了,于是我们可以想到,对于dep相同的点,我们都for两遍,第一遍遍历与之dep相同的点并更新,第二遍再更新第二种情况,这样就可以完美解决问题了。

代码

#include<bits/stdc++.h>
using namespace std;
long long mod=1e9+7;
int n,m;
int s,t;
const int maxn=200005;
struct EDGE
{
    int next,to;
}edge[maxn<<1];
int head[maxn];
int cnt;
void add(int u,int v)
{
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
struct node
{
    int id,dep;
};
int dep[maxn];
bool vis[maxn];
vector<int>dots;
queue<node>q;
long long dp1[maxn],dp2[maxn];
int main()
{
    memset(head,-1,sizeof(head));
    int _;
    scanf("%d",&_);
    while(_--)
    {
        scanf("%d%d",&n,&m);
        scanf("%d%d",&s,&t);
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        q.push({s,0});
        dots.push_back(s);
        vis[s]=1;
        function<void()>bfs1=[&]()
        {
            while(!q.empty())
            {
                auto [x,Dep]=q.front();
                q.pop();
                for(int i=head[x];~i;i=edge[i].next)
                {
                    int it=edge[i].to;
                    if(!vis[it])
                    {
                        dep[it]=Dep+1;
                        vis[it]=1;
                        dots.push_back(it);
                        q.push({it,Dep+1});
                    }
                }
            }
        };
        bfs1();
        dp1[s]=1;
        int siz=dots.size();
        for(int i=0;i<siz;)
        {
            int j=i;
            while(j<siz&&dep[dots[i]]==dep[dots[j]])j++;
            for(int k=i;k<j;k++)
            {
                for(int l=head[dots[k]];~l;l=edge[l].next)
                {
                    int it=edge[l].to;
                    if(dep[dots[k]]==dep[it])dp2[it]=(dp2[it]+dp1[dots[k]])%mod;

                }
            }
            for(int k=i;k<j;k++)
            {
                for(int l=head[dots[k]];~l;l=edge[l].next)
                {
                    int it=edge[l].to;
                    if(dep[dots[k]]+1==dep[it])
                    {
                        dp1[it]=(dp1[it]+dp1[dots[k]])%mod;
                        dp2[it]=(dp2[it]+dp2[dots[k]])%mod;
                    }
                }
            }
            i=j;
        }
        printf("%lld\n",(dp1[t]+dp2[t])%mod);
        if(_)
        {
            cnt=0;
            for(int i=1;i<=n;i++)
            {
                dep[i]=0;
                vis[i]=0;
                head[i]=-1;
                dp1[i]=dp2[i]=0;
            }
            for(int i=0;i<cnt;i++)
            {
                edge[i].to=edge[i].next=0;
            }
            dots.clear();
            while(!q.empty())q.pop();
        }
    }
    return 0;
}

__EOF__

本文作者Jerry-Black
本文链接https://www.cnblogs.com/Jerry-Black/p/16008034.html
关于博主:小蒟蒻一只( ̄^ ̄)ゞ
版权声明:转载请注明来源哟~ QAQ
声援博主:UP UP UP !!!
posted @   Jerry_Black  阅读(176)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示