题解 洛谷P6413 [COCI2008-2009#3] NAJKRACI

Posted 漠寒·

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 洛谷P6413 [COCI2008-2009#3] NAJKRACI相关的知识,希望对你有一定的参考价值。

链接

分析

计算出最短路后,一条边是最短路的一部分,当且仅当起点的 \\(f\\) 值加上该边边权等于终点的 \\(f\\) 值,所以跑最短路后,对 \\(m\\) 条边进行判定,满足该条件的加入最短路图。

加入后进行拓扑排序,计算以该边作为终点的最短路个数 \\(cntz\\),和该边作为起点的最短路 \\(cntq\\),易证两子问题的计算互为倒序,拓扑排序只需跑起点到终点即可,跑的过程中记录顺序,在倒着顺序把 \\(cntz\\) 加回来,最后将两值相乘,即为答案。

即每一次 \\(cntm_v+=cntm_u\\)\\(cntq_u+=cntq_v\\)\\(ans_i+=cntm_i \\times cntq_i\\)

另外值得注意的是,因为是不断换起点跑最短路,\\(dijkstra\\) 的复杂度可能因 \\(memset\\) 过多而时间较长,\\(spfa\\) 会低一些,虽然本题时间上限很高,但当然越优越好。

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,head[1501],vis[1501],flag[5001],f[1501],cnt,ans[5001],sum[1501],cntm[1501],cntq[1501];
const int mod=1e9+7;
struct node{
	int to,w,fr,next;
}a[5001];
void read(int &res){
	char c;
	res=0;
	c=getchar();
	while(c<\'0\'||c>\'9\'){c=getchar();}
	while(c>=\'0\'&&c<=\'9\')res=(res<<1)+(res<<3)+c-48,c=getchar();
}
priority_queue<pair<int,int> > q;
/*
inline void dijkstra(int qq){
	memset(vis,0,sizeof(vis));
	memset(flag,0,sizeof(flag));
	memset(f,127,sizeof(f));
	f[qq]=0;
	q.push(make_pair(0,qq));
	while(q.size()){
		int x=q.top().second;q.pop();
		if(vis[x])continue;
		vis[x]=1;
		for(register int i=head[x];i;i=a[i].next){
			int v=a[i].to;
			if(f[v]>f[x]+a[i].w){
				f[v]=f[x]+a[i].w;
				q.push(make_pair(-f[v],a[i].to));
			}
		}
	}
	for(register int i=1;i<=m;++i){
		if(f[a[i].fr]+a[i].w==f[a[i].to])flag[i]=1;
	}
}
*/

int que[10001],len;

inline void spfa(int qq) {
    memset(f,127,sizeof(f));
    memset(flag,0,sizeof(flag));
    f[que[len=1]=qq]=0;
    for (register int i = 1; i <= len; i++) {
        int x=que[i];vis[x]=0;
        for (register int i=head[x];i;i=a[i].next){
        	int v=a[i].to;
            if(f[x]+a[i].w<f[v]){
            	f[v]=f[x]+a[i].w;
            	if(!vis[v])vis[que[++len]=v]=1;
			}
		}
    }
    for(register int i=1;i<=m;++i){
		if(f[a[i].fr]+a[i].w==f[a[i].to])flag[i]=1;//满足条件,进行标记 
	}
}

int qu[1501],tot;
inline void topu(int qq){
	memset(sum,0,sizeof(sum));
	memset(cntm,0,sizeof(cntm));
	memset(cntq,0,sizeof(cntq));
	for(register int i=1;i<=m;++i)if(flag[i])sum[a[i].to]++;
	cntm[qq]=1;
	qu[tot=1]=qq;
	for(register int i=1;i<=tot;++i){
		int x=qu[i];
		for(register int i=head[x];i;i=a[i].next){
			if(!flag[i])continue;
			int v=a[i].to;
			if(!--sum[v])qu[++tot]=v;
			cntm[v]=(cntm[v]+cntm[x])%mod;
		}
	}
	for(register int i=tot;i;i--){
		int x=qu[i];cntq[x]++;
		for(register int i=head[x];i;i=a[i].next){
			if(!flag[i])continue;
			cntq[x]=(cntq[x]+cntq[a[i].to])%mod;
		}
	}
}

inline void sol(int qq){
	spfa(qq);topu(qq);
	for(register int i=1;i<=m;++i){
		if(flag[i])ans[i]=(ans[i]+1ll*cntm[a[i].fr]*cntq[a[i].to]%mod)%mod;
	}
}

inline void add(int qq,int mm,int l){
	a[++cnt].fr=qq;
	a[cnt].next=head[qq];
	head[qq]=cnt;
	a[cnt].to=mm;
	a[cnt].w=l;
}
int main()
{
	read(n);read(m);
	for(int i=1;i<=m;i++){
		int x,y,j;
		read(x);read(y);read(j);
		add(x,y,j);
	}
	for(int i=1;i<=n;i++)sol(i);
	for(int i=1;i<=m;i++){
		printf("%d\\n",ans[i]);
	}
	return 0;
}

以上是关于题解 洛谷P6413 [COCI2008-2009#3] NAJKRACI的主要内容,如果未能解决你的问题,请参考以下文章

题解 P6467 [COCI2008-2009#6] BUKA

洛谷题解P4326 求圆的面积

题解Dvoniz [COCI2011]

[COCI2011-2012#4] OGRADA 题解

题解 P7619 [COCI2011-2012#2] RASPORED

COCI 20152016 1st round 题解(官方)