1. 程式人生 > >【BZOJ5109】[CodePlus 2017]大吉大利,晚上吃雞! 最短路+拓撲排序+DP

【BZOJ5109】[CodePlus 2017]大吉大利,晚上吃雞! 最短路+拓撲排序+DP

image truct getc https 絕地求生 我們 mes iterator ==

【BZOJ5109】[CodePlus 2017]大吉大利,晚上吃雞!

Description

最近《絕地求生:大逃殺》風靡全球,皮皮和毛毛也迷上了這款遊戲,他們經常組隊玩這款遊戲。在遊戲中,皮皮和毛毛最喜歡做的事情就是堵橋,每每有一個好時機都能收到不少的快遞。當然,有些時候並不能堵橋,皮皮和毛毛會選擇在其他的必經之路上蹲點。K博士作為一個老年人,外加有心臟病,自然是不能玩這款遊戲的,但是這並不能妨礙他對這款遊戲進行一些理論分析,比如最近他就對皮皮和毛毛的戰士很感興趣。【題目描述】遊戲的地圖可以抽象為一張n個點m條無向邊的圖,節點編號為1到n,每條邊具有一個正整數的長度。假定大魔王都會從S點出發到達T點(S和T已知),並且只會走最短路,皮皮和毛毛會在A點和B點埋伏大魔王。 為了保證一定能埋伏到大魔王,同時又想留大魔王一條生路,皮皮和毛毛約定A點和B點必須滿足: 1.大魔王所有可能路徑中,必定會經過A點和B點中的任意一點 2.大魔王所有可能路徑中,不存在一條路徑同時經過A點和B點 K博士想知道,滿足上面兩個條件的A,B點對有多少個,交換A,B的順序算相同的方案

Input

第一行輸入四個整數n,m,S,T(1≤n≤5×10^4,1≤m≤5×10^4,1≤S,T≤n),含義見題目描述。 接下來輸入m行,每行輸入三個整數u,v,w(1≤u,v≤n,1≤w≤10^9)表示存在一條長度為w的邊鏈接u和v。 1≤n≤5×10^4,1≤m≤5×10^4,1≤w≤10^9

Output

輸出一行表示答案

Sample Input

7 7 1 7
1 2 2
2 4 2
4 6 2
6 7 2
1 3 2
3 5 4
5 7 2

Sample Output

6
【樣例 1 解釋】
合法的方案為 < 2, 3 >, < 2, 4 >, < 4, 3 >, < 4, 5 >, < 6, 3 >, < 6, 5 > 。

題解:第一思路是先隨便找出一條最短路,那麽最終的A點和B點一定有一個在這條最短路上,我們設在路徑上的是A。於是我們枚舉所有點B,考慮它可以搭配哪些合法的點A。 不難發現,為了滿足條件2,可以選擇的點A一定在一段區間中(如果能從B走到A,那麽B也一定能走到A後面的點;如果A能走到B,那麽A前面的點也一定能走到B),我們可以先求出最短路徑圖,然後在正圖和反圖上分別跑拓撲排序+DP,就能得出每個B的合法A區間。

那麽條件1如何滿足呢?我們可以用拓撲排序求出經過點i的最短路徑條數f[i],那麽如果A和B滿足條件1,等價於f[A]+f[B]=f[T],所以我們可以采用差分的方式,將每個B的f值扔到對應的A區間中,然後枚舉所有A,用map維護當前有多少個點的f值等於一個數,每枚舉到一個A就查詢一下有多少個點的f等於f[T]-f[A]即可。不過f值可能很大,我們可以采用取模的方式,如果感覺還是很虛的話,可以多取幾個模數(本人取了兩個)。

但是,考試時寫了一發只有55分,為什麽?45分的數據S和T都不連通,此時要輸出$C_n^2$!輸出$C_n^2$能得45分也就算了,我後來check了一下數據,發現所有圖都是隨機的,所有數據中從S到T的最短路最多只有1條!所以呢,本題其實只需要先特判S和T是否連通,若不連通則輸出$C_n^2$,否則隨便找一條S到T的最短路,設路徑上的點數為len,輸出len*(n-len)即能得到滿分。

技術分享圖片

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <utility>
#include <map>
#include <vector>
#define mp(A,B) make_pair(A,B)
using namespace std;
typedef long long ll;
const int maxn=50010;
const ll P1=998244353;
const ll P2=1000000007;
int n,m,cnt,len,S,T;
ll ans;
struct node
{
	ll x,y;
	node() {}
	node(ll a,ll b) {x=a,y=b;}
	node operator + (const node &a) const {return node((x+a.x)%P1,(y+a.y)%P2);}
	node operator * (const node &a) const {return node(x*a.x%P1,y*a.y%P2);}
	node operator - (const node &a) const {return node((x-a.x+P1)%P1,(y-a.y+P2)%P2);}
	bool operator < (const node &a) const {return (x==a.x)?(y<a.y):(x<a.x);}
}f1[maxn],f2[maxn],f[maxn];
priority_queue<pair<ll,int> > pq;
queue<int> q;
int to[maxn<<1],next[maxn<<1],head[maxn],vis[maxn],d[maxn],pre[maxn],lm[maxn],rm[maxn],p[maxn];
//l正r反
ll val[maxn<<1],s1[maxn],s2[maxn];
map<node,int> s;
vector<node>::iterator it;
vector<node> p1[maxn],p2[maxn];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
inline void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
int main()
{
	//freopen("7.in","r",stdin);
	n=rd(),m=rd(),S=rd(),T=rd();
	int i,j,a,b,c,u,v;
	memset(head,-1,sizeof(head)),memset(s1,0x3f,sizeof(s1)),memset(s2,0x3f,sizeof(s2));
	for(i=1;i<=m;i++)	a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c);
	s1[S]=0,pq.push(mp(0,S));
	while(!pq.empty())
	{
		u=pq.top().second,pq.pop();
		if(vis[u])	continue;
		vis[u]=1;
		for(i=head[u];i!=-1;i=next[i])	if(s1[to[i]]>s1[u]+val[i])
			s1[to[i]]=s1[u]+val[i],pq.push(mp(-s1[to[i]],to[i]));
	}
	if(s1[T]==0x3f3f3f3f3f3f3f3fll)
	{
		printf("%lld",(ll)n*(n-1)/2);
		return 0;
	}
	s2[T]=0,pq.push(mp(0,T)),memset(vis,0,sizeof(vis));
	while(!pq.empty())
	{
		u=pq.top().second,pq.pop();
		if(vis[u])	continue;
		vis[u]=1;
		for(i=head[u];i!=-1;i=next[i])	if(s2[to[i]]>s2[u]+val[i])
			s2[to[i]]=s2[u]+val[i],pre[to[i]]=u,pq.push(mp(-s2[to[i]],to[i]));
	}
	for(i=S;i;i=pre[i])	p[++len]=i,lm[i]=len+1,rm[i]=len-1;
	for(i=1;i<=n;i++)	if(!lm[i])	lm[i]=1,rm[i]=len;
	for(i=1;i<=n;i++)	for(j=head[i];j!=-1;j=next[j])	if(val[j]>0&&s1[i]+s2[to[j]]+val[j]==s1[T])
		val[j]=-1,val[j^1]=-2,d[to[j]]++;
	for(i=1;i<=n;i++)	if(!d[i])	q.push(i);
	f1[S]=node(1,1),f2[T]=node(1,1);
	while(!q.empty())
	{
		u=q.front(),q.pop();
		for(i=head[u];i!=-1;i=next[i])	if(val[i]==-1)
		{
			v=to[i],d[v]--,f1[v]=f1[v]+f1[u],lm[v]=max(lm[v],lm[u]);
			if(!d[v])	q.push(v);
		}
	}
	for(i=1;i<=n;i++)	for(j=head[i];j!=-1;j=next[j])	if(val[j]==-2)	d[to[j]]++;
	for(i=1;i<=n;i++)	if(!d[i])	q.push(i);
	while(!q.empty())
	{
		u=q.front(),q.pop();
		for(i=head[u];i!=-1;i=next[i])	if(val[i]==-2)
		{
			v=to[i],d[v]--,f2[v]=f2[v]+f2[u],rm[v]=min(rm[v],rm[u]);
			if(!d[v])	q.push(v);
		}
	}
	for(i=1;i<=n;i++)
	{
		f[i]=f1[i]*f2[i];
		if(lm[i]<=rm[i])	p1[lm[i]].push_back(f[i]),p2[rm[i]].push_back(f[i]);
	}
	for(i=1;i<=len;i++)
	{
		for(it=p1[i].begin();it!=p1[i].end();it++)	s[*it]++;
		ans+=s[f[T]-f[p[i]]];
		for(it=p2[i].begin();it!=p2[i].end();it++)	s[*it]--;
	}
	printf("%lld",ans);
	return 0;
}

【BZOJ5109】[CodePlus 2017]大吉大利,晚上吃雞! 最短路+拓撲排序+DP