题意简述
给定一个无向图(点数n<=5e4n<=5e4n<=5e4,边数m<=1e5m<=1e5m<=1e5),其中有k(k<=n)k(k<=n)k(k<=n)个草堆节点。(kkk行,每行两个数viv_ivi,yiy_iyi表示第iii个草堆在viv_ivi点上,美味值是yiy_iyi)求111到n−1n-1n−1中有多少节点满足:
经过一个草堆节点到nnn的最短路-这个草堆节点的美味值<=直接到节点nnn的最短路。
输出n−1n-1n−1行,第iii个节点要是能满足上述条件,就输出111,否则输出000。(真程序员)
数据
输入
4 5 1
1 4 10
2 1 20
4 2 3
2 3 5
4 3 2
2 7
输出
1
1
1
思路
首先我们要知道nnn到每个节点的最短路吧。。。这个不知道怎么算后面的。。。而且也没有理由不知道,常数不大,数据又小,不会耗费太多时间计算。这样也就算出了nnn到每个草堆节点的距离(每个节点都算了,肯定包含了所有的草堆节点)。(这就告诉我们要先打好Dijkstra的板子)
接下来我们要知道的就是所有草堆节点到所有别的节点的最短路了。有一个很好 暴力的思路,就是每个作起点跑一遍DijkstraDijkstraDijkstra。这样就FFTFFTFFT(即fast−fast−TLEfast-fast-TLEfast−fast−TLE)了。显然这样是不珂以的。我们有没有什么好一点的办法珂以一遍DijDijDij求出所有草堆的最小值呢?
经过一节生物课,一节政治课,终于,我还是没有思路,只能回来看stdstdstd。正解就是:建一个"超级起点"(网络流题经常这么干,但我怎么也没有想到一个最短路题会这么干),设其编号为n+1n+1n+1,然后让这个点连接所有的草堆节点。那么,这kkk条边中每条边的权值是多少呢?珂以设置成0么?不珂以。。。如果设置成000的话我们只能求出经过某个草垛的最短路,但减去美味值的时候,就不知道减哪个了。。。因为没记录。。。所以考虑这个边权就顺带起记录作用,也就是:
点n−1n-1n−1连接每一个草堆节点iii,边有向!有向!有向!边权是dis1[i]−yum[i]dis1[i]-yum[i]dis1[i]−yum[i],yum[i]yum[i]yum[i]表示这个点的美味值,dis1[i]dis1[i]dis1[i]表示第一次DijkstraDijkstraDijkstra求出的iii到nnn的最短路。
这样就差不多明白怎么写了,其实就稍微比DijkstraDijkstraDijkstra模板代码多一点点而已。代码:
#include<bits/stdc++.h>
#define N 1001000
#define int long long
using namespace std;
class Graph//图
{
private:
int head[N];
int EdgeCount=0;
public:
struct Edge
{
int To,Label,Next;
}Ed[N<<1];
void clear()
{
memset(Ed,-1,sizeof(Ed));
memset(head,-1,sizeof(head));
EdgeCount=0;
}
void AddEdge(int u,int v,int w)
{
++EdgeCount;
Ed[EdgeCount]=(Edge){v,w,head[u]};
head[u]=EdgeCount;
}
int Start(int i)
{
return head[i];
}
int To(int i)
{
return Ed[i].To;
}
int Label(int i)
{
return Ed[i].Label;
}
int Next(int i)
{
return Ed[i].Next;
}
}G;
struct node
{
int v,w;
const bool operator<(const node CompWith) const
{
return w>CompWith.w;
}
};
priority_queue<node>Q;
bool vis[N];
void DijkstraLog(int dis[N],int s)//nlogn的超快Dijkstra
{
memset(vis,0,sizeof(vis));
for(int i=0;i<N;++i) dis[i]=0x3f3f3f3f3f3f3f3f;
Q=priority_queue<node>();
dis[s]=0;
Q.push((node){s,0});
while(!Q.empty())
{
node Minnode=Q.top();Q.pop();
int u=Minnode.v;
if (vis[u]) continue;
vis[u]=1;
for(int i=G.Start(u);~i;i=G.Next(i))
{
int v=G.To(i),len=G.Label(i);
if (!vis[v] and ~v and dis[v]>dis[u]+len)
{
dis[v]=dis[u]+len;
Q.push((node){v,dis[v]});
}
}
}
}
int dis1[N],dis2[N];
int yum[N];
int n,m,k;
void Add(int u,int v,int w,bool directed=0)
{
G.AddEdge(u,v,w);
if (!directed)
{
G.AddEdge(v,u,w);
}
}
void Input()
{
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=m;++i)
{
int u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
Add(u,v,w);
}
for(int i=1;i<=k;++i)
{
int pos,val;
scanf("%lld%lld",&pos,&val);
yum[pos]=val;
}
}
void Solve()
{
DijkstraLog(dis1,n);
for(int i=1;i<=n;++i)
{
if (yum[i])//判断i是否是草堆节点
{
Add(n+1,i,dis1[i]-yum[i],1);
}
}
DijkstraLog(dis2,n+1);
for(int i=1;i<n;i++)
{
if (dis1[i]>=dis2[i])//如果满足条件
{
puts("1");
}
else//不满足条件
{
puts("0");
}
}
}
main()
{
G.clear();
Input();
Solve();
return 0;
}