zoukankan      html  css  js  c++  java
  • 【BZOJ5109】[CodePlus 2017]大吉大利,晚上吃鸡! 最短路+拓扑排序+DP

    【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;
    }
  • 相关阅读:
    判断activity是否显示在界面上
    限制EditText的输入字数
    安卓自定义类似TabHost的导航栏
    安卓中加载布局文件的三种方法
    绘图——Android绘图基础:Canvas、Paint等
    使用简单图片
    使用原始资源
    MySQL分表(Partition)学习研究报告
    Docker基础知识介绍
    Python开发系列
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7905974.html
Copyright © 2011-2022 走看看