[CF464E] The Classic Problem
链接
首先,假如不考虑时间复杂度,直接高精度边权+最短路显然是对的,但是这样会达到 \(O(m\log n\times x)\) 的时间复杂度。
考虑如何优化 \(O(x)\) 的查询时间。题目中特别说明了一条边的边权一定是 \(2^x\) 。而我们需要的操作是将某一位 \(+1\) 并进位,同时查询两个数中的较大值。
对于前者,可以发现我们只需要完成:区间推平,单点修改,求从某一位 \(p\) 开始往后第一个0的位置。显然这可以用一颗线段树维护。对于询问只需合并左右子树的信息,如果左子树全是1就再考虑右子树,否则直接返回左子树的第一个0的位置。
对于后者,可以用类似于 \(Hash\) 二分求 \(lcp\) 的做法,即如果两棵树的右子树的 \(Hash\) 值相等,说明右子树完全相同,直接递归查询左子树,否则查询右子树。
注意修改操作时也要修改节点的 \(Hash\) 值。
由于每个节点对于转移节点来说最多只会进行一次修改,显然需要可持久化,即继承转移节点没有修改的信息。
接下来套进最短路的板子里即可。复杂度 \(O(m\log n\ \log x)\)。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define ll long long
#define mod 1000000007
#define N 105010
#define M N*200
#define base 233
#define ull unsigned long long
using namespace std;
ull bs[N<<1];
int ls[M],rs[M],val[M],tot;
bool tag[M];
ull hs[M];
int new_node(int p)
{
int u=++tot;
if(!p) return u;
ls[u]=ls[p],rs[u]=rs[p],val[u]=val[p];
tag[u]=tag[p],hs[u]=hs[p];
return u;
}
void update(int u,int l,int r)
{
int mid=(l+r)>>1;
hs[u]=hs[ls[u]]+hs[rs[u]]*bs[mid-l+1];
val[u]=val[ls[u]]==(mid-l+1)?val[ls[u]]+val[rs[u]]:val[ls[u]];
}
void push_down(int u)
{
if(!tag[u]) return;
tag[ls[u]=new_node(ls[u])]=true;
hs[ls[u]]=val[ls[u]]=0;
tag[rs[u]=new_node(rs[u])]=true;
hs[rs[u]]=val[rs[u]]=0;
tag[u]=false;
}
int insert(int p,int l,int r,int pos)
{
push_down(p);
int u=new_node(p);
if(l==r){val[u]=hs[u]=1;return u;}
int mid=(l+r)>>1;
if(pos<=mid) ls[u]=insert(ls[u],l,mid,pos);
else rs[u]=insert(rs[u],mid+1,r,pos);
update(u,l,r);
return u;
}
int reset(int p,int l,int r,int L,int R)
{
int u=new_node(p);
if(L>R) return u;
if(L<=l && r<=R){val[u]=hs[u]=0;tag[u]=true;return u;}
int mid=(l+r)>>1;
if(L<=mid) ls[u]=reset(ls[u],l,mid,L,R);
if(R>mid) rs[u]=reset(rs[u],mid+1,r,L,R);
update(u,l,r);
return u;
}
int answer(int u,int l,int r,int p)
{
if(!u || tag[u]) return 0;
if(l>=p) return val[u];
int mid=(l+r)>>1;
if(p>mid) return answer(rs[u],mid+1,r,p);
else
{
int v=answer(ls[u],l,mid,p);
if(v==mid-p+1) return v+val[rs[u]];
else return v;
}
}
int n=N;
int cmp(int u,int v)
{
int fu=u,fv=v;
int l=0,r=n;
while(1)
{
if(!u || tag[u]) return fv;
if(!v || tag[v]) return fu;
if(l==r) return hs[u]<=hs[v]?fv:fu;
int mid=(l+r)>>1;
if(hs[rs[u]]==hs[rs[v]]) u=ls[u],v=ls[v],r=mid;
else u=rs[u],v=rs[v],l=mid+1;
}
}
int add_one(int p,int v)
{
int l=answer(p,0,n,v);
int u=reset(p,0,n,v,v+l-1);
u=insert(u,0,n,v+l);
return u;
}
struct pqnode{
int u;
int rt;
bool operator <(const pqnode a)const{return cmp(rt,a.rt)==rt;}
};
priority_queue<pqnode>q;
int nxt[M],to[M],wx[M],head[N],cnt;
void add(int u,int v,int w)
{
nxt[++cnt]=head[u];
to[cnt]=v;
wx[cnt]=w;
head[u]=cnt;
}
int drt[N],pre[N];
ll ksm(int a,int b)
{
ll r=1;
for(;b;b>>=1)
{
if(b&1) r=1ll*r*a%mod;
a=1ll*a*a%mod;
}
return r;
}
ll dfs(int u,int l,int r)
{
if(!u || tag[u]) return 0;
if(l==r) return 1ll*val[u]*ksm(2,l)%mod;
int mid=(l+r)>>1;
return (dfs(ls[u],l,mid)+dfs(rs[u],mid+1,r))%mod;
}
int fa[N],res[N],tres;
bool vis[N];
void dij(int s,int t)
{
drt[s]=new_node(0);
q.push((pqnode){s,drt[s]});
while(!q.empty())
{
int u=q.top().u;
q.pop();
if(vis[u]) continue;
vis[u]=true;
if(u==t)
{
printf("%lld\n",dfs(drt[u],0,n));
for(;u!=s;u=fa[u])res[++tres]=u;
res[++tres]=u;
printf("%d\n",tres);
for(int i=tres;i;i--) printf("%d ",res[i]);
return;
}
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
int k=add_one(drt[u],wx[i]);
if(!vis[v] && (!drt[v] || cmp(k,drt[v])==drt[v]))
{
drt[v]=k;
fa[v]=u;
q.push((pqnode){v,drt[v]});
}
}
}
puts("-1");
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
bs[0]=1;
for(int i=1;i<N;i++) bs[i]=bs[i-1]*base;
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w),add(v,u,w);
}
int s,t;
scanf("%d%d",&s,&t);
dij(s,t);
return 0;
}
本文来自博客园,作者:Flying2018,转载请注明原文链接:https://www.cnblogs.com/Flying2018/p/13232653.html