题目大意:
题目链接:https://jzoj.net/senior/#main/show/6357
求点1到点
n
n
n的最大与路径。
思路:
吧每一条边的距离看成二进制。
显然如果存在一条二进制下最高位为第
k
k
k位为1的路径,那么最高位为第
k
+
1
k+1
k+1位的路径都没有这条路径优秀。
所以我们考虑按二进制下每一位来看。从高位到低位枚举,然后再枚举每一条边,如果这条边的这一位为1,那么就把这条边所连接的两个点划分到一个集合内。
如果最终点1和点
n
n
n再一个集合内,那么说明可以,就把所有边权这一位为0的边标记一下,以后就不找了。然后
a
n
s
∣
=
2
i
ans|=2^i
ans∣=2i。
时间复杂度
O
(
n
log
(
a
)
)
O(n\log(a))
O(nlog(a))
代码:
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=100010,M=500010;
int n,m,tot,father[N],head[N];
ll ans;
struct edge
{
int next,to,from;
ll dis;
bool flag;
}e[M];
ll read()
{
ll d=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch))
d=(d<<1)+(d<<3)+ch-48,ch=getchar();
return d;
}
void add(int from,int to,ll dis)
{
e[++tot].to=to;
e[tot].from=from;
e[tot].dis=dis;
e[tot].flag=1;
e[tot].next=head[from];
head[from]=tot;
}
int Find(int x)
{
return x==father[x]?x:father[x]=Find(father[x]);
}
int main()
{
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
memset(head,-1,sizeof(head));
n=read(); m=read();
for (int i=1;i<=m;i++)
{
int x=read(); int y=read(); ll d=read();
add(x,y,d);
}
for (ll s=1LL<<62;s;s>>=1)
{
for (int i=1;i<=n;i++)
father[i]=i;
for (int i=1;i<=m;i++)
if (e[i].flag && (e[i].dis&s))
{
int u=Find(e[i].from),v=Find(e[i].to);
if (u!=v) father[u]=v;
}
if (Find(1)==Find(n))
{
ans|=s;
for (int i=1;i<=m;i++)
if (!(e[i].dis&s)) e[i].flag=0;
}
}
printf("%lld",ans);
return 0;
}