时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
有n个城市,有m条连接两个城市的道路,每条道路有一个初始(第0天)的泥泞程度
z
i
z_i
zi,每天泥泞程度都会增加1。
现在有Q组询问,每组询问问你第i天从1号城市到 j j j号城市所经过道路的泥泞程度总和最少为多少。
输入
第一行三个数, n,m,Q(n<=1000,m<=2000,Q<=600000)
接下来m行,每行三个数 x i , y i , z i x_i,y_i,z_i xi,yi,zi表示城市 x i x_i xi和 y i y_i yi之间有一条初始泥泞程度为 z i z_i zi的道路( z i z_i zi<=1000)
接下来Q行,每行两个数, j j j和 i i i,表示一组询问(0 <= i <= 1000000000)
输出
对于每组询问输出一个数表示答案
样例输入
3 3 2
1 2 1
2 3 1
1 3 10
3 0
3 100
样例输出
2
110
思路:因为路的泥泞度会发生变化,所以最短路跟边的数量也有关系,从样例即可看出。
令
d
[
i
]
[
j
]
=
d[i][j]=
d[i][j]=从点1出发恰好经过
j
j
j条边到达点
i
i
i的最短路
(
1
≤
j
≤
n
)
(1\le j\le n)
(1≤j≤n)。
这个可以利用最短路算法求出。
那么经过 x x x天后到达 i i i点的最短距离 a n s = m i n ( d [ i ] [ j ] + j ∗ x ) , 1 ≤ j ≤ m ans=min(d[i][j]+j*x),1\le j\le m ans=min(d[i][j]+j∗x),1≤j≤m。
如果查询时,暴力枚举的话复杂度可能会达到 O ( m Q ) O(mQ) O(mQ)。
可以发现 a n s = d [ i ] [ j ] + j ∗ x ans=d[i][j]+j*x ans=d[i][j]+j∗x这个式子可以利用斜率优化,使 a n s ans ans的取值在一个抛物线上。
然后查询就可以利用三分来求答案了。
对于评论的小伙伴我作了个图解释一下。
我们先考虑目的地固定的情况,假设所有询问的目的地均为点
n
n
n,
我们可以先画出一个简单的坐标系,那么对于对于不同的天数
x
x
x,点
(
x
,
d
[
n
]
[
x
]
)
(x,d[n][x])
(x,d[n][x])在二维坐标系中的分布图大致如下。并可以先对这些点求一个下半部分的凸包。
那么当第
k
k
k天时,
a
n
s
=
m
i
n
(
d
[
n
]
[
x
]
+
k
∗
x
)
(
1
<
=
x
<
=
m
)
ans=min(d[n][x]+k*x)(1<=x<=m)
ans=min(d[n][x]+k∗x)(1<=x<=m)。
设
b
=
d
[
n
]
[
x
]
+
k
∗
x
=
y
+
k
∗
x
b=d[n][x]+k*x=y+k*x
b=d[n][x]+k∗x=y+k∗x,那么
a
n
s
=
m
i
n
(
a
n
s
,
b
)
ans=min(ans,b)
ans=min(ans,b)。
上式转换过来就是
y
=
k
∗
x
+
b
y=k*x+b
y=k∗x+b。
y
=
d
[
n
]
[
x
]
y=d[n][x]
y=d[n][x]和
x
x
x都已知。
那么我们要求的就是对于经过点
(
x
,
d
[
n
]
[
x
]
)
(x,d[n][x])
(x,d[n][x]),且斜率为
k
k
k的直线的
b
b
b值,而当
x
=
0
x=0
x=0时,我们可以得到
b
b
b值。如下图。
由于斜率
k
k
k不变,而
x
x
x可以变化,我们可以平移直线
y
=
k
∗
x
+
b
y=k*x+b
y=k∗x+b,使其与点
(
x
,
d
[
n
]
[
x
]
)
(x,d[n][x])
(x,d[n][x])相交。
那么显而易见,当直线越往下平移时,
b
b
b值越小,那么我们更新得到的
a
n
s
ans
ans也越小。而这里我们可以用之前求得的凸包,根据斜率来三分得到最小的
b
b
b值。
代码里的 q q q数组存储的就是凸包里的点的 x x x坐标值。
#include<bits/stdc++.h>
using namespace std;
const int MAX=1e3+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
struct lenka
{
int now,step;
ll cost;
bool operator<(const lenka& A)const{return A.cost<cost;}
};
vector<pair<int,int> >e[MAX];
ll d[MAX][2*MAX];
int n,m;
void bfs()
{
priority_queue<lenka>p;
p.push({1,0,0});
while(!p.empty())
{
lenka A=p.top();p.pop();
if(A.cost!=d[A.now][A.step])continue;
if(A.step==n)continue;
for(int i=0;i<e[A.now].size();i++)
{
pair<int,int>nex=e[A.now][i];
if(d[nex.first][A.step+1]==-1||d[nex.first][A.step+1]>A.cost+nex.second)
{
d[nex.first][A.step+1]=A.cost+nex.second;
p.push({nex.first,A.step+1,A.cost+nex.second});
}
}
}
}
int q[MAX][2*MAX];
int up[MAX];
int main()
{
int Q;
cin>>n>>m>>Q;
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
e[x].push_back({y,z});
e[y].push_back({x,z});
}
memset(d,-1,sizeof d);
d[1][0]=0;
bfs();
for(int i=2;i<=n;i++)
{
int R=0;
for(int j=1;j<=m;j++)
{
if(d[i][j]==-1)continue;
while(R>1&&(d[i][j]-d[i][q[i][R-1]])*1ll*(j-q[i][R-2])<(d[i][j]-d[i][q[i][R-2]])*1ll*(j-q[i][R-1]))R--;
q[i][R]=j;
R++;
}
up[i]=R-1;
}
while(Q--)
{
int x,y;
scanf("%d%d",&x,&y);
if(x==1){puts("0");continue;}
int l=0,r=up[x];
while(r-l>1)
{
int mid=(l+r)/2;
int mm=(r+mid)/2;
if(d[x][q[x][mid]]+1ll*q[x][mid]*y>d[x][q[x][mm]]+1ll*q[x][mm]*y)l=mid;
else r=mm;
}
printf("%lld\n",min(d[x][q[x][l]]+1ll*q[x][l]*y,d[x][q[x][r]]+1ll*q[x][r]*y));
}
return 0;
}