[主席树][哈希][最短路]CF464E The Classic Problem

题面

https://codeforces.com/contest/464/problem/E

https://www.luogu.com.cn/problem/CF464E

分析

高精度暴力跑最短路 $O(nlognx)$ ,考虑优化

发现最短路中松弛操作所用的加法,在二进制串上所需要实现的就是区间清零和单点修改

e.g. 01110+00100=10010

考虑用线段树维护01串,找一段连续 1 区间可以用线段树二分解决,比较大小的时候可以用类似字符串哈希的做法来确定高位是否相同

但时间复杂度 $O(nlogx)$ 空间复杂度 $O(n^2logx)$ 不能通过

考虑到有许多共用节点,将线段树改为主席树

区间清零可以通过建一个全零线段树,将需要清零的区间的父亲连到对应零区间,辅以一些回收节点的操作即可通过

时间复杂度 $O(nlogx)$ 空间复杂度 $O(nlog^2x)$

代码

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll P=1e9+7;
const int N=2e5+10;
const int M=N*20;
struct Graph {
    int v,w,nx;
}g[2*N];
int cnt,list[N],tcnt,root[N],f[N];
struct Node {
    ll val;
    ull hash;
    int cnt,c[2];
    friend bool operator == (Node a,Node b) {return a.val==b.val&&a.hash==b.hash&&a.cnt==b.cnt;}
}t[2*M];
ll pw[N];
ull hnum[N];
bool vis[N];
int n,m,inf,S,T,ans[N],acnt;

void Add(int u,int v,int w) {g[++cnt]=(Graph){v,w,list[u]};list[u]=cnt;}

void Update(int x) {
    t[x].cnt=t[t[x].c[0]].cnt+t[t[x].c[1]].cnt;
    t[x].val=(t[t[x].c[0]].val+t[t[x].c[1]].val)%P;
    t[x].hash=t[t[x].c[0]].hash+t[t[x].c[1]].hash;
}

void Build(int &x,int l,int r,int val) {
    if (!x) x=++tcnt;
    if (l==r) return t[x].cnt=val,t[x].val=val*pw[l],t[x].hash=val*hnum[l],void();
    int mid=l+r>>1;
    Build(t[x].c[0],l,mid,val);Build(t[x].c[1],mid+1,r,val);
    Update(x);
}

int Query(int x,int l,int r,int L,int R) {
    if (L<=l&&r<=R) return t[x].cnt;
    int mid=l+r>>1,ans=0;
    if (L<=mid) ans=Query(t[x].c[0],l,mid,L,R);
    if (mid<R) ans+=Query(t[x].c[1],mid+1,r,L,R);
    return ans;
}

int Search(int x,int l,int r,int k) {
    if (l==r) return l;
    int mid=l+r>>1;
    if (k>mid) return Search(t[x].c[1],mid+1,r,k);
    if (Query(t[x].c[0],l,mid,k,mid)==mid-k+1) return Search(t[x].c[1],mid+1,r,mid+1);
    return Search(t[x].c[0],l,mid,k);
}

bool S_CMP(int x,int y,int l,int r) {
    if (l==r) return t[x].cnt<t[y].cnt;
    int mid=l+r>>1;
    if (t[t[x].c[1]]==t[t[y].c[1]]) return S_CMP(t[x].c[0],t[y].c[0],l,mid);
    return S_CMP(t[x].c[1],t[y].c[1],mid+1,r);
}

void Link(int &x,int y,int l,int r,int L,int R) {
    if (L<=l&&r<=R) return x=y,void();
    int mid=l+r>>1,z=++tcnt;t[z]=t[x];
    if (L<=mid) Link(t[z].c[0],t[y].c[0],l,mid,L,R);
    if (mid<R) Link(t[z].c[1],t[y].c[1],mid+1,r,L,R);
    Update(x=z);
}

void S_Add(int &x,int l,int r,int k) {
    t[++tcnt]=t[x];x=tcnt;
    if (l==r) return t[x].val=pw[l],t[x].cnt=1,t[x].hash=hnum[l],void();
    int mid=l+r>>1;
    if (k<=mid) S_Add(t[x].c[0],l,mid,k);
    else S_Add(t[x].c[1],mid+1,r,k);
    Update(x);
}

struct Path {
    int id,rt;
    friend bool operator < (Path a,Path b) {return S_CMP(b.rt,a.rt,0,N-1);}
};
priority_queue<Path> q;

void Dijkstra() {
    priority_queue<Path> q;
    while (!q.empty()) q.pop();
    for (int i=1;i<=n;i++) root[i]=inf;root[S]=root[0];q.push((Path){S,root[S]});
    while (!q.empty()) {
        int u=q.top().id;q.pop();
        if (vis[u]) continue;vis[u]=1;
        for (int i=list[u];i;i=g[i].nx) {
            int newz=root[u],l=Search(root[u],0,N-1,g[i].w),re=tcnt;
            S_Add(newz,0,N-1,l);
            if (g[i].w<l) Link(newz,root[0],0,N-1,g[i].w,l-1);
            if (S_CMP(newz,root[g[i].v],0,N-1)) f[g[i].v]=u,q.push((Path){g[i].v,root[g[i].v]=newz});
            else tcnt=re;
        }
    }
}

int main() {
    scanf("%d%d",&n,&m);
    for (int i=1,u,v,x;i<=m;i++) scanf("%d%d%d",&u,&v,&x),Add(u,v,x),Add(v,u,x);
    scanf("%d%d",&S,&T);
    pw[0]=1;hnum[0]=1;for (int i=1;i<N;i++) pw[i]=(pw[i-1]<<1)%P,hnum[i]=hnum[i-1]*23;
    Build(root[0],0,N-1,0);Build(inf,0,N-1,1);
    Dijkstra();
    if (root[T]==inf) return printf("-1\n"),0;
    for (int x=T;x!=S;x=f[x]) ans[++acnt]=x;
    ans[++acnt]=S;
    printf("%lld\n%d\n",t[root[T]].val,acnt);
    for (int i=acnt;i;i--) printf("%d ",ans[i]);
}
View Code

 

posted @ 2021-04-01 11:31  Vagari  阅读(94)  评论(3编辑  收藏  举报