刷题总结——做运动(NOIP模拟)
题目:
给定一个无向图,节点数n<=50000,m<=1000000,每条边有两个值t和c,边的长度为t*c···现在要求再t尽量小的情况下,求两节点st的最短距离
题解:
第一次做的时候想都没有想直接用二分+迪杰斯特拉了···哎连复杂度都算不来了···
正解应该是将边按t升序排序后跑kruskals····用并差集判st是否连通··一旦联通将t值小于等于目前枚举的t值的边全部加入建图然后跑最短路就可以了···
其实如果考试时仔细想一想时是可以想到正解的··毕竟如果t要尽量小的话不是二分就是最小生成树了···但并差集判连通性这一点已经很久每用过了··这题算是提了醒吧··
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> #include<queue> using namespace std; priority_queue< pair<long long,int> >que; const int N=5e5+5; const int M=1e6+5; inline int R() { char c;int f=0; for(c=getchar();c<'0'||c>'9';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f; } struct node { int x,y,T,C; inline friend bool operator < (node a,node b) { return a.T<b.T; } }edge[M]; int Father[N],fst[N],go[M*2],nxt[M*2],n,m,src,des,anst,tot; long long dis[N],val[M*2]; inline bool comb(int a,int b,int c,int d) { nxt[++tot]=fst[a],fst[a]=tot,go[tot]=b,val[tot]=(long long)c*d; nxt[++tot]=fst[b],fst[b]=tot,go[tot]=a,val[tot]=(long long)c*d; } inline int get(int a) { if(Father[a]==a) return a; else return Father[a]=get(Father[a]); } inline void solve() { for(register int i=1;i<=n;i++) dis[i]=2e+18; dis[src]=0;que.push(make_pair(0,src)); while(!que.empty()) { int u=que.top().second;que.pop(); if(u==des) break; for(register int e=fst[u];e;e=nxt[e]) { int v=go[e]; if(dis[v]>dis[u]+val[e]) { dis[v]=dis[u]+val[e]; que.push(make_pair(-dis[v],v)); } } } } int main() { //freopen("a.in","r",stdin); n=R();m=R(); for(register int i=1;i<=n;i++) Father[i]=i; for(register int i=1;i<=m;i++) edge[i].x=R(),edge[i].y=R(),edge[i].T=R(),edge[i].C=R(); src=R();des=R(); sort(edge+1,edge+m+1); for(register int i=1;i<=m;i++) { int fx=get(edge[i].x),fy=get(edge[i].y); Father[fx]=fy; if(get(src)==get(des)) { anst=edge[i].T; break; } } cout<<anst<<" "; for(register int i=1;i<=m;i++) { if(edge[i].T>anst) break; comb(edge[i].x,edge[i].y,edge[i].T,edge[i].C); } solve();cout<<dis[des]<<endl; return 0; }