HAOI2012 道路

题目链接 戳我

最短路DAG上计数

经过点的最短路计数和经过边的最短路计数实在不能一概而论。。。。qwqwqwqwqwq

在做这道题之前,让我先捞上一张来自xyz32768大佬的图

对于一条边(x,y)——求S到x的最短路径个数是很好求的一件事,对于一条边两个端点u,v,sum[v]+=sum[u]。那么顺序应当怎么确定呢?拓扑排序啊!但是怎么保证正确性呢?没有环啊!

求上面那个好求,但是求y到T的最短路径个数就不太容易了。。。因为我们必须保证这些最短路都经过S->x,x->y,而不是从S直接到了y。还按照上面那个方法的话是有后效性的,无法DP。

但是!但是!我们没有办法正着DP,难道就不能反过来了吗!sum[u]+=sum[v]不就可以了吗?

至此,大功告成。

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define MAXN 100010
#define mod 1000000007
using namespace std;
int n,m,t,tot,ll,rr;
int cnt[MAXN],cnt1[MAXN],cnt2[MAXN],done[MAXN];
int head[MAXN],ans[MAXN],ins[MAXN],dis[MAXN],pq[MAXN];
struct Edge{int nxt,to,dis;}edge[MAXN<<1];
struct Line{int u,v,w;}a[MAXN];
int q[MAXN];
inline void add(int from,int to,int dis)
{
    edge[++t].nxt=head[from],edge[t].to=to;
    edge[t].dis=dis,head[from]=t;
}
inline void spfa(int x)
{
    memset(dis,0x3f,sizeof(dis));
    memset(ins,0,sizeof(ins));
    ll=1,rr=0;
    q[++rr]=x;dis[x]=0;done[x]=1;
    while(ll<=rr)
    {
        int u=q[ll++];done[u]=0;
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(dis[u]+edge[i].dis<dis[v])
            {
                dis[v]=dis[u]+edge[i].dis;
                if(!done[v])
                    q[++rr],done[v]=1;
            }
        }
    }
    for(int i=1;i<=m;i++)
        if(dis[a[i].u]+a[i].w==dis[a[i].v])
            ins[i]=1;
}
inline void topo_sort(int x)
{
    memset(cnt,0,sizeof(cnt));
    memset(cnt1,0,sizeof(cnt1));
    memset(cnt2,0,sizeof(cnt2));
    ll=1,rr=0,tot=0;
    cnt1[x]=1;
    q[++rr]=x;
    for(int i=1;i<=m;i++) if(ins[i]) cnt[a[i].v]++;
    while(ll<=rr)
    {
        int u=q[ll++];
        pq[++tot]=u;
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(!ins[i]) continue;
            cnt[v]--;
            if(!cnt[v]) q[++rr]=v;
            cnt1[v]=(cnt1[v]+cnt1[u])%mod;
        }
    }
    for(int i=tot;i;i--)
    {
        int u=pq[i]; cnt2[u]++;
        for(int j=head[u];j;j=edge[j].nxt)
        {
            if(!ins[j]) continue;
            int v=edge[j].to;
            cnt2[u]=(cnt2[u]+cnt2[v])%mod;
        }
    }
}
inline void print()
{
    for(int i=1;i<=n;i++) printf("ins[%d]=%d\n",i,ins[i]);
    for(int i=1;i<=n;i++) printf("dis[%d]=%d\n",i,dis[i]);
    for(int i=1;i<=n;i++) printf("cnt1[%d]=%d\n",i,cnt1[i]);
    for(int i=1;i<=n;i++) printf("cnt2[%d]=%d\n",i,cnt2[i]);
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        a[i].u=u,a[i].v=v,a[i].w=w;
        add(u,v,w);
    }
    for(int i=1;i<=m;i++) 
    {
        printf("i=%d\n",i);
        spfa(i);
        topo_sort(i);
        print();
        for(int i=1;i<=m;i++)
            if(ins[i])
                ans[i]=(ans[i]+1ll*cnt1[a[i].u]*cnt2[a[i].v]%mod)%mod;
    }
    for(int i=1;i<=m;i++)
            printf("%d\n",ans[i]);
    return 0;
}

呜呜呜好吧我承认这个代码在BZOJ上没有办法过。。。会T一个点。。。。。。但是把STL队列手写一下。。应该就可以了。。。。。。
不开O2惨无人道啊qwqwq

猜你喜欢

转载自www.cnblogs.com/fengxunling/p/10462240.html