[JZOJ5909]【NOIP2018模拟10.16】跑商【圆方树】【树链剖分】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/83091348

Description

基三的地图可以看做 n 个城市,m 条边的无向图,尊者神高达会从某个点出发并在起点购买货物,在旅途中任意一点卖出并最终到达终点,尊者神高达的时间很宝贵,所以他不会重复经过同一个城市,但是为了挣钱,他可能会去绕路。当然,由于工作室泛滥,所以一个城市的货物价格可能会发生改变。但是尊者神高达智商不足,他可能在一个很蠢的节点把货物卖掉,所以尊者神高达想知道每一次跑商最多能赔多少钱。
数据满足 n,m,q<=100000;保证图联通,数据合法

Solution

由于买的地方是固定的(起点),因此我们相当于要求所有路径中经过的点权最大值
点权最小,并且不能经过重复的点。

很容易想到点双联通分量
只要一个点双联通分量在路径上,那么它里面的所有点我们都是有路径经过的

将圆方树构出来,一个点双建一个方点,点双中所有点连向这个方点。
方点上记录点双中点权最小值
那么就变成了路径查询问题
但是现在有修改

考虑更改方点维护的值,我们对于一个方点只维护它在圆方树上所有儿子的点权最小值(即不维护父亲的),带修改就用set维护
那么这样每个圆点唯一对应一个方点,修改也只用改自己和父亲的方点。

查询路径时,我们发现只有路径的LCA会受到影响,因此只需要判断LCA是否为方点,为方点的话再加入它父亲那个圆点的点权。
查询路径最大值可以用树链剖分。

总的复杂度 O ( n log 2 n ) O(n \log^2 n)

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <set>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
#define LL long long
using namespace std;
int m1,n,m,pr[N],n1,fs[N],nt[2*N],dt[2*N],q,dfn[N],low[N],fx[2*N],fi[N],nx[2*N],d1[2*N],top[N],m2,dep[N],st[N],f[N],sz[N],son[N];
int t[2*N][2],ft[2*N],pt[N],n2,mi[2*N];
bool bz[N];
multiset<int> h[N];
void link(int x,int y)
{
	nt[++m1]=fs[x];
	dt[fs[x]=m1]=y;
}
void lk(int x,int y)
{
	nx[++m2]=fi[x];
	d1[fi[x]=m2]=y;
}
void tarjan(int k,int fa)
{
	st[++st[0]]=k;
	low[k]=dfn[k]=++dfn[0];
	for(int i=fs[k];i;i=nt[i])
	{
		int p=dt[i];
		if(p!=fa)
		{
			if(!dfn[p])
			{
				tarjan(p,k);
				if(low[p]>=dfn[k])
				{
					lk(++n1,k),lk(k,n1);
					while(st[st[0]]!=p) lk(n1,st[st[0]]),lk(st[st[0]],n1),st[st[0]--]=0;
					lk(n1,p),lk(p,n1),st[st[0]--]=0;
				}
				else low[k]=min(low[k],low[p]);
			}
			else low[k]=min(low[k],dfn[p]);
		}
	}
}
void dfs(int k,int fa)
{
	if(k<=n&&fa>n) h[fa].insert(pr[k]);
	dep[k]=dep[fa]+1;
	f[k]=fa;
	sz[k]=1;
	for(int i=fi[k];i;i=nx[i])
	{
		int p=d1[i];
		if(p!=fa) 
		{
			dfs(p,k),sz[k]+=sz[p];
			if(sz[p]>sz[son[k]]) son[k]=p;
		}
	}
}
void build(int k,int l,int r)
{
	mi[k]=1e9;
	if(l==r) pt[l]=k;
	else
	{
		int mid=(l+r)>>1;
		ft[t[k][0]=++n2]=k,build(n2,l,mid);
		ft[t[k][1]=++n2]=k,build(n2,mid+1,r);
	}
}
void ins(int k,int v)
{
	mi[k]=v,k=ft[k];
	while(k) mi[k]=min(mi[t[k][0]],mi[t[k][1]]),k=ft[k];
}
int query(int k,int l,int r,int x,int y)
{
	if(!k||x>y||x>r||y<l) return 1e9;
	if(x<=l&&r<=y) return mi[k];
	int mid=(l+r)>>1;
	return min(query(t[k][0],l,mid,x,y),query(t[k][1],mid+1,r,x,y));	
}
void make(int k)
{
	if(!k) return;
	dfn[k]=++dfn[0];
	if(k>n) pr[k]=*h[k].begin();
	ins(pt[dfn[k]],pr[k]);
	top[son[k]]=top[k],make(son[k]);
	for(int i=fi[k];i;i=nx[i])
	{
		int p=d1[i];
		if(p!=f[k]&&p!=son[k]) top[p]=p,make(p);
	}
}
int get(int x,int y)
{	
	if(top[x]==top[y]) 
	{
		if(dep[x]>dep[y]) swap(x,y);
		int v=1e9;
		if(x>n) v=pr[f[x]];
		return min(v,query(1,1,n1,dfn[x],dfn[y]));
	}
	if(dep[top[x]]>dep[top[y]]) swap(x,y);
	return min(get(x,f[top[y]]),query(1,1,n1,dfn[top[y]],dfn[y]));
}
int main()
{
	cin>>n>>m;
	fo(i,1,n) scanf("%d",&pr[i]);
	fo(i,1,m)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		link(x,y),link(y,x);
		fx[fx[m1]=m1-1]=m1;
	}
	n1=n;
	tarjan(1,0);
	dfs(1,0);
	n2=1;
	memset(dfn,0,sizeof(dfn));
	build(1,1,n1);
	make(1);
	cin>>q;
	fo(i,1,q)
	{
		char ch;
		scanf("\n%c ",&ch);
		int x,y;
		scanf("%d%d",&x,&y);
		if(ch=='Q') printf("%d\n",pr[x]-get(x,y));
		else
		{
			ins(pt[dfn[x]],y);
			if(x>1) 
			{
				int p=f[x];
				h[p].erase(h[p].find(pr[x]));
				h[p].insert(y);
				int v=*h[p].begin();
				if(v!=pr[p]) 
				{
					pr[p]=v;
					ins(pt[dfn[p]],pr[p]);
				}
			}
			pr[x]=y;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/hzj1054689699/article/details/83091348