原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1863.html
题目传送门 - 51Nod1863
题意
有 n 个城市,有 m 条双向路径连通它们。
每一个城市有一个属性,可能有多个城市有相同的属性。
给定属性的优先级,求一条从 1 到 n 的路径,满足优先级最大的属性在这条路径中出现次数尽量小,在此基础上,优先级次大的出现次数尽量小,以此类推。
问这条路径经过了哪些属性的节点,按照优先级从高到低输出。
$nleq 10^5,mleq 5 imes 10^5$
题解
首先需要往最短路方向想这题。
每个点的 dis 可以表示成一个按排序表顺序的数量序列,第 i 个元素表示优先级为 i 的属性的出现次数。
每经过一条边,就相当于单点加一,可以用主席树维护。
那么如何判定两个序列的大小?
我们需要找到的是这两个序列的第一个不同元素。
主席树上维护区间哈希值来判断区间是否相同,然后二分即可。
因为需要用主席树维护,后面的状态基于前面的,所以不能 spfa 。
只能 dijkstra 。但是空间复杂度是 $O(mlog n)$ ,会 MLE 。
我们发现每一次修改的时候,由于一个点自身的属性不会变,所以每次修改这个点的时候,一定是从一个状态继承,并修改 其属性的 rank 这个位置上的值,所以每次新建的节点都是一样的,所以直接覆盖到原先建出来的节点上即可,空间复杂度 $O(nlog n)$ 。
堆优化 dijkstra ,时间复杂度 $O(nlog ^2 n)$ 。(比较大小需要一个 log)
我偷了个懒用 set 当堆,居然只跑了不到 1.3s 。
代码
#include <bits/stdc++.h> #define clr(x) memset((x),0,sizeof (x)) using namespace std; int read(){ int x=0; char ch=getchar(); while (!isdigit(ch)) ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } const int N=100005; int n,m; int lis[N],rk[N],be[N]; vector <int> e[N]; int root[N]; namespace pt{ const int S=N*50; int ls[S],rs[S],v[S]; int Pow[N],T,mod,cnt; void build(int &rt,int L,int R){ rt=++cnt; if (L==R) return; int mid=(L+R)>>1; build(ls[rt],L,mid); build(rs[rt],mid+1,R); } void init(int n,int _T,int _mod){ T=_T,mod=_mod,cnt=0; for (int i=Pow[0]=1;i<=n;i++) Pow[i]=1LL*Pow[i-1]*T%mod; clr(ls),clr(rs),clr(v); build(root[0],1,n); } void update(int prt,int &rt,int L,int R,int x){ if (!rt) rt=++cnt; if (L==R) return (void)(v[rt]=v[prt]+1); int mid=(L+R)>>1; if (x<=mid) update(ls[prt],ls[rt],L,mid,x),rs[rt]=rs[prt]; else update(rs[prt],rs[rt],mid+1,R,x),ls[rt]=ls[prt]; v[rt]=(1LL*v[ls[rt]]*Pow[R-mid]+v[rs[rt]])%mod; } int ADD(int x,int y){ return x+y>=mod?x+y-mod:x+y; } int cmp(int r1,int r2,int L,int R,int x=0,int d=0){ if (!r2) return -1; if (L==R){ if (x<L||x>R) d=0; if (v[r1]+d==v[r2]) return 0; return v[r1]+d<v[r2]?-1:1; } int mid=(L+R)>>1; if ((L<=x&&x<=mid&&d?ADD(Pow[mid-x],v[ls[r1]]):v[ls[r1]])!=v[ls[r2]]) return cmp(ls[r1],ls[r2],L,mid,x,d); else return cmp(rs[r1],rs[r2],mid+1,R,x,d); } int query(int rt,int L,int R,int x){ if (L==R) return v[rt]; int mid=(L+R)>>1; int ans=0; if (x<=mid) ans=query(ls[rt],L,mid,x); else ans=query(rs[rt],mid+1,R,x); v[rt]=(1LL*v[ls[rt]]*Pow[R-mid]+v[rs[rt]])%mod; return ans; } } struct Node{ int id; Node(int _id=0){ id=_id; } friend bool operator < (Node a,Node b){ int v=pt :: cmp(root[a.id],root[b.id],1,n); return (v!=0)?(v<0):(a.id<b.id); } }; set <Node> S; int vis[N],del[N]; int main(){ n=read(),m=read(); for (int i=1;i<=n;i++) rk[lis[i]=read()]=i; for (int i=1;i<=n;i++) be[i]=read(); for (int i=1;i<=m;i++){ int a=read(),b=read(); e[a].push_back(b); e[b].push_back(a); } pt :: init(n,233,998244353); pt :: update(root[0],root[1],1,n,rk[be[1]]); S.clear(); S.insert(Node(1)); vis[1]=1; for (int t=n-1;t--&&!S.empty();){; int x=(*S.begin()).id; S.erase(S.begin()); del[x]=1; for (auto y : e[x]){ if (del[y]) continue; if (pt :: cmp(root[x],root[y],1,n,rk[be[y]],1)<0){ if (vis[y]) S.erase(Node(y)); pt :: update(root[x],root[y],1,n,rk[be[y]]); S.insert(Node(y)); vis[y]=1; } } } clr(vis); for (int i=1;i<=n;i++) vis[i]=pt :: query(root[n],1,n,i); for (int i=1;i<=n;i++) while (vis[i]--) printf("%d ",lis[i]); return 0; }