zoukankan      html  css  js  c++  java
  • 51Nod1863 Travel 主席树 最短路 Dijkstra 哈希

    原文链接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;
    }
    

      

  • 相关阅读:
    react 入坑笔记(五)
    练习
    高级指令
    进阶指令
    基础指令
    VMware 备份操作系统
    Ubuntu 用户的切换
    形态学转换
    图像模糊
    域名拆分 tld
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/51Nod1863.html
Copyright © 2011-2022 走看看