题意:有一个无向图,每条边的权值为2^x(0<=x<=1e5),求 s 到 t 的最短路。
思路:最短路好搞,关键是这个权值不好比较大小,由于x<=1e5,但是我们发现从一个点 u 到 v,其距离只增加了2^a[x],那么我们可以用可持久化线段树保存新增的这个信息,每个点的最短路都用一颗线段树保存,线段树第 i 个叶子节点保存的是是否有 2^i 的权值,那么我们怎么比较两个数大小呢?对于d[ u ],d[ v ],我从这两颗线段树同时往叶子节点找,从右区间开始,通过hash找到第一个不相等的点,就可以用log的复杂度判断两个数大小,还有一个问题,怎么更新线段树,假设线段树d[u]保存了11101(左边低位,右边高位),d[v]比d[u]多了2^0,那么我从第0位开始找到第一个为0的点 k =3,然后把 [ 0 , 2 ]区间的1抹去,并把第3位的0修改为1就可以得到d[v]这颗线段树。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+30,mod=1e9+7,mod2=998244353;
int ls[maxn*30],rs[maxn*30],mn[maxn*30],sum[maxn*30];
int vis[maxn],P[maxn],path[maxn],cnt,mx=1e5+30;
int ha[maxn*30],p[maxn],sz[maxn];
vector<int>G[maxn],dis[maxn];
struct node
{
int u,o;
node(){}
node(int a,int b){u=a,o=b;}
bool operator<(const node&t)const
{
int l=1,r=mx,o1=o,o2=t.o;
while(l!=r)
{
int m=(l+r)/2;
if(ha[rs[o1]]!=ha[rs[o2]]||sum[rs[o1]]!=sum[rs[o2]])
{
l=m+1;
o1=rs[o1];
o2=rs[o2];
}
else
{
r=m;
o1=ls[o1];
o2=ls[o2];
}
}
return ha[o1]>ha[o2];
}
}d[maxn];
priority_queue<node>q;
int qu(int o,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)return mn[o];
int m=(l+r)/2,res=1e9;
if(ql<=m)res=min(res,qu(ls[o],l,m,ql,qr));
if(qr>m)res=min(res,qu(rs[o],m+1,r,ql,qr));
return res;
}
void pushup(int o)
{
mn[o]=min(mn[ls[o]],mn[rs[o]]);
ha[o]=(1ll*ha[ls[o]]+ha[rs[o]])%mod2;
sum[o]=(1ll*sum[ls[o]]+sum[rs[o]])%mod;
}
void up(int &o,int pre,int l,int r,int k,int v)
{
o=++cnt;
ls[o]=ls[pre];
rs[o]=rs[pre];
if(l==r)
{
ha[o]=p[l]*v;
sum[o]=P[l]*v;
if(v)mn[o]=1e9;
else mn[o]=l;
return;
}
int m=(l+r)/2;
if(k<=m)up(ls[o],ls[pre],l,m,k,v);
else up(rs[o],rs[pre],m+1,r,k,v);
pushup(o);
}
void up2(int &o,int pre,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)
{
o=0;
return;
}
o=++cnt;
ls[o]=ls[pre];
rs[o]=rs[pre];
int m=(l+r)/2;
if(ql<=m)up2(ls[o],ls[pre],l,m,ql,qr);
if(qr>m)up2(rs[o],rs[pre],m+1,r,ql,qr);
pushup(o);
}
int Query(int rt,int k)
{
int l=k,r=mx;
while(l<r)
{
int m=(l+r)/2;
if(qu(rt,0,mx,k,m))
l=m+1;
else r=m;
}
return l;
}
int dij(int n,int s,int t)
{
for(int i=1;i<=n;i++)
d[i].u=i,d[i].o=0;
q.push(d[s]);
sz[s]=1;
while(!q.empty())
{
int u=q.top().u;q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i],w=dis[u][i];
if(vis[v])continue;
node e=node(v,0);
int k=Query(d[u].o,w);
if(k>w)
up2(e.o,d[u].o,0,mx,w,k-1);
if(!e.o)up(e.o,d[u].o,0,mx,k,1);
else up(e.o,e.o,0,mx,k,1);
if(d[v].o&&(e<d[v]||(ha[e.o]==ha[d[v].o]&&sum[e.o]==sum[d[v].o])))
continue;
sz[v]=sz[u]+1;
path[v]=u;
d[v].o=e.o;
q.push(d[v]);
}
}
if(!vis[t])
return -1;
return sum[d[t].o];
}
void init()
{
p[0]=P[0]=1;
for(int i=1;i<maxn;i++)
{
p[i]=1ll*p[i-1]*43%mod2;
P[i]=1ll*P[i-1]*2%mod;
}
}
void dfs(int u)
{
if(!u)return;
dfs(path[u]);
printf("%d ",u);
}
int main()
{
init();
int n,m,u,v,w,s,t;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
G[u].push_back(v);
G[v].push_back(u);
dis[u].push_back(w);
dis[v].push_back(w);
}
scanf("%d%d",&s,&t);
int ans=dij(n,s,t);
printf("%d\n",ans);
if(ans!=-1)
{
printf("%d\n",sz[t]);
dfs(t);
}
}