「洛谷 4366」最短路

传送门


problem

给定一个 n n n 个点的完全图,边 ( u , v ) (u,v) (u,v) 的权是 ( u    x o r    v ) × C (u\;\mathrm{xor}\;v)\times C (uxorv)×C C C C 是一个常数。

在这个完全图的基础上再加上 m m m有向边,问从 S S S 点到 T T T 点的最短路是多少。

数据范围: n ≤ 1 0 5 n\le 10^5 n105 m ≤ 5 × 1 0 5 m\le5\times 10^5 m5×105


solution

建边的思路应该比较好想。

对于每个点,我们向其二进制中各个位反转后的点连一条 2 i × C 2^i\times C 2i×C 的边( i i i 是当前位)。

比如,对于 011 011 011,我们分别向 111 111 111 001 001 001 010 010 010 连边。

那么,如果要从 011 011 011 转移到 101 101 101,可以看成是 011 → 111 → 101 011\rightarrow111\rightarrow101 011111101,花费了 2 C 2C 2C 的代价,与连边是吻合的。

这样总共有 O ( n log ⁡ n + m ) O(n\log n+m) O(nlogn+m) 条边,我开了 O 2 O2 O2 才跑过的。


code

#include<bits/stdc++.h>
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=(x+(x<<2)<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=1e5+5,M=5e6+5;
int n,m,C,t,S,T;
int first[N],v[M],w[M],nxt[M];
void add(int x,int y,int z){
	nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=z;
}
void build(){
	for(int i=0;i<=n;++i)
		for(int j=0;j<17;++j)
			add(i,i^(1<<j),C*(1<<j));
}
int d[N];
typedef pair<int,int> pii;
priority_queue<pii>Q;
void dijkstra(int S){
	memset(d,127/3,sizeof(d));
	Q.push(pii(0,S)),d[S]=0;
	while(!Q.empty()){
		int x=Q.top().second;Q.pop();
		for(int i=first[x];i;i=nxt[i]){
			int to=v[i];
			if(d[to]>d[x]+w[i])  d[to]=d[x]+w[i],Q.push(pii(-d[to],to));
		}
	}
}
int main(){
	n=in(),m=in(),C=in();
	for(int i=1,x,y,z;i<=m;++i){
		x=in(),y=in(),z=in(),add(x,y,z);
	}
	build();
	S=in(),T=in(),dijkstra(S);
	printf("%d\n",d[T]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值