【CF464E】The Classic Problem(主席树优化高精,dijkstra)

题目也没有给出什么很特殊的性质,所以考虑能不能高精度最短路。

dijkstra 中涉及的运算主要是两个:加法和比较大小。

注意到题目给出的边权是 2 k 2^k 2k 的形式,我们不如直接用二进制表示每一个数。

而比较大小的实质是从高到低找到第一个不相等的位,这个我们可以用哈希+二分实现。

发现加法并不是两个大数相加,而是一个大数 x x x 加上一个 2 m 2^m 2m(代码中的具体体现是 dis[u]+w[i]),这样做的是实质是从 x x x 的第 m m m 位开始找到最长的一段 1 1 1,并把这一段 1 1 1 置为 0 0 0,把下一个 0 0 0 置为 1 1 1。也就是区间置零和单点修改。

容易联想到用线段树维护每一个数,而每次相加找最长的连续的 1 1 1 可以在线段树上二分,区间置零和单点修改只需要可持久化一下就好(加法在 dijkstra 最多只会进行 m m m 次)。同时第一个比较大小也可以变成在线段树上二分。

还有一个小细节就是我们先可以建一棵全零树,然后区间置零时直接连向全零树的对应区间就行。

代码如下:

#include<bits/stdc++.h>

#define LN 17
#define N 100050
#define mod 1000000007
#define lc(u) t[u].ch[0]
#define rc(u) t[u].ch[1]

using namespace std;

const unsigned int base=19260817;

int n,m,S,T;
int cnt,head[N],to[N<<1],w[N<<1],nxt[N<<1];
int node,rt0,rt1;
int pre[N],dis[N];
bool vis[N];
int pow2[N];
unsigned int poww[N];
int top,sta[N];

struct Node
{
	int ch[2];
	bool sum;
	unsigned int x; 
}t[N*8+N*LN*4];

void up(int u,int len)
{
	t[u].sum=t[lc(u)].sum&t[rc(u)].sum;
	t[u].x=t[lc(u)].x+poww[len]*t[rc(u)].x;
}

void build(int &u,int l,int r,bool val)
{
	u=++node;
	if(l==r)
	{
		t[u].sum=val;
		t[u].x=val;
		return;
	}
	int mid=(l+r)>>1;
	build(lc(u),l,mid,val);
	build(rc(u),mid+1,r,val);
	up(u,mid-l+1);
}

int compare(int a,int b,int l,int r)
{
	if(l==r)
	{
		if(t[a].sum==t[b].sum) return 0;
		if(t[a].sum) return 1;
		if(t[b].sum) return -1;
	}
	int mid=(l+r)>>1;
	if(t[rc(a)].x!=t[rc(b)].x) return compare(rc(a),rc(b),mid+1,r);
	return compare(lc(a),lc(b),l,mid);
}

int find2(int u,int l,int r)
{
	if(t[u].sum) return r;
	if(l==r) return -1;
	int mid=(l+r)>>1;
	int lans=find2(lc(u),l,mid);
	if(lans==mid)
	{
		int rans=find2(rc(u),mid+1,r);
		if(rans!=-1) return rans;
	}
	return lans;
}

int find1(int u,int l,int r,int x)
{
	if(x<=l) return find2(u,l,r);
	int mid=(l+r)>>1;
	if(x<=mid)
	{
		int lans=find1(lc(u),l,mid,x);
		if(lans==mid)
		{
			int rans=find1(rc(u),mid+1,r,x);
			if(rans!=-1) return rans;
		}
		return lans;
	}
	return find1(rc(u),mid+1,r,x);
}

void update1(int &a,int b,int c,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr)
	{
		a=c;
		return;
	}
	a=++node;
	t[a]=t[b];
	int mid=(l+r)>>1;
	if(ql<=mid) update1(lc(a),lc(b),lc(c),l,mid,ql,qr);
	if(qr>mid) update1(rc(a),rc(b),rc(c),mid+1,r,ql,qr);
	up(a,mid-l+1);
}

void update2(int &a,int b,int l,int r,int x)
{
	a=++node;
	t[a]=t[b];
	if(l==r)
	{
		t[a].sum=1;
		t[a].x=1;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) update2(lc(a),lc(b),l,mid,x);
	else update2(rc(a),rc(b),mid+1,r,x);
	up(a,mid-l+1);
}

int add(int rt,int x)
{
	int tmp=find1(rt,0,1e5+17,x);
	int nrt1=rt;
	if(tmp!=-1) update1(nrt1,rt,rt0,0,1e5+17,x,tmp);
	else tmp=x-1;
	int nrt2;
	update2(nrt2,nrt1,0,1e5+17,tmp+1);
	return nrt2;
}

struct data
{
	int u,rt;
	data(){};
	data(int a,int b){u=a,rt=b;}
	bool operator < (const data &a) const
	{
		return compare(rt,a.rt,0,1e5+17)>0;
	}
};

priority_queue<data>q;

void adde(int u,int v,int xi)
{
	to[++cnt]=v;
	w[cnt]=xi;
	nxt[cnt]=head[u];
	head[u]=cnt;
}

void dijkstra()
{
	for(int i=1;i<=n;i++) dis[i]=rt1;
	dis[S]=rt0;
	q.push(data(S,dis[S]));
	while(!q.empty())
	{
		data now=q.top();
		q.pop();
		if(vis[now.u]) continue;
		vis[now.u]=1;
		for(int i=head[now.u];i;i=nxt[i])
		{
			int v=to[i];
			int rta=add(now.rt,w[i]);
			if(compare(rta,dis[v],0,1e5+17)<0)
			{
				dis[v]=rta,pre[v]=now.u;
				q.push(data(v,dis[v]));
			}
		}
	}
}

int getsum(int u,int l,int r)
{
	if(l==r) return t[u].sum*pow2[l];
	int mid=(l+r)>>1,ans=0;
	if((ans+=getsum(lc(u),l,mid))>=mod) ans-=mod;
	if((ans+=getsum(rc(u),mid+1,r))>=mod) ans-=mod;
	return ans;
}

int main()
{
	pow2[0]=poww[0]=1;
	for(int i=1;i<=1e5+17;i++)
	{
		pow2[i]=(pow2[i-1]<<1)%mod;
		poww[i]=poww[i-1]*base;
	}
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int u,v,x;
		scanf("%d%d%d",&u,&v,&x);
		adde(u,v,x),adde(v,u,x);
	}
	scanf("%d%d",&S,&T);
	build(rt0,0,1e5+17,0);
	build(rt1,0,1e5+17,1);
	dijkstra();
	if(!vis[T])
	{
		puts("-1");
		return 0;
	}
	printf("%d
",getsum(dis[T],0,1e5+17));
	for(int u=T;u!=S;u=pre[u]) sta[++top]=u;
	sta[++top]=S;
	printf("%d
",top);
	for(int i=top;i>=1;i--)
		printf("%d ",sta[i]);
	return 0;
}
/*
2 1
1 2 1
1 2
*/
原文地址:https://www.cnblogs.com/ez-lcw/p/14448626.html