题解:P10206 [JOI 2024 Final] 建设工程 2 / Construction Project 2
闲话
这个题目有点板子吧,小技巧比较多。
正文
一眼看到这个题目,我就想到了特判,当从
好了,特判完,就来想想正解。
先考虑一个拆分的小技巧:对于从
这个技巧有什么用呢?
很简单,我们可以考虑枚举
显然,我们用
因为我们枚举了 upper_bound就可以做到这个。
所以代码具体就是:
- 分别从
S 点出发和从T 点出发得到dis(S,u) 和dis(v,T) 。 - 对
dis(v,T) 排序。 - 对于每个
u 点,二分查找满足条件的v 点的数量,将答案累加,最后输出。
但是有问题啊!
想想,如果对于一对
那根据上述的算法,会在枚举到
但是我没考虑到上面的情况,依然通过了这道题,说明数据过水上面的情况是不成立的,考虑简单证明一下。
采用反证法,假设存在一对
但我们将前面四项两两匹配:
明显就是:
把系数消去:
则:
明显这里已经被我们在一开始特判掉了,所以我们不满足算重的条件,也就不会算重。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define V vector
#define FOR(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define pb push_back
const int INF=1e18+10;
using P=array<int,2>;
struct node{
int id,dis;
friend bool operator <(const node &a,const node &b){
return a.dis>b.dis;
}
};
V<int>dij(V<V<P> >&e,int st,int n){
V<int>dis(n+1,INF);V<bool>vis(n+1,false);
priority_queue<node>q;q.push({st,0});dis[st]=0;
while(!q.empty()){
int u=q.top().id;q.pop();
if(vis[u])continue;
vis[u]=true;
for(auto i:e[u]){
int v=i[0],w=i[1];
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
q.push({v,dis[v]});
}
}
}
return dis;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n,m,s,t,l,k;
cin>>n>>m>>s>>t>>l>>k;
V<V<P> >e(n+1);
FOR(i,1,m){
int a,b,c;
cin>>a>>b>>c;
e[a].pb({b,c});
e[b].pb({a,c});
}
V<int>dis1=dij(e,s,n),dis2=dij(e,t,n);
if(dis1[t]<=k){
cout<<(n-1)*n/2;
return 0;
}
V<int>dis3=dis2;
int ans=0;
sort(dis3.begin()+1,dis3.end());
// FOR(i,1,n) cout<<dis3[i]<<" ";
// cout<<"\n";
FOR(i,1,n){
int sum=upper_bound(dis3.begin()+1,dis3.end(),k-l-dis1[i])-dis3.begin()-1;
ans+=sum;
}
cout<<ans;
return 0;
}