平衡树

最近半期考试比较忙,因此部分代码没有写,以后补上。

优势人群(Efficient Solutions, UVa11020)

description

给出(n) 个物品,每个物品有两个特征值(L,C) 。定义物品(i) 比物品(j) 优秀当且仅当满足以下条件至少一条

  • (L_i<L_j)(C_ile C_j)
  • (L_ile L_j)(C_i< C_j)

称一个物品是优秀的当且仅当不存在其他物品比它优秀。

现在要求每加入一个物品,输出有多少物品是优秀的。

solution

如果我们每个物品(i) 看作平面上的点,坐标为((L_i,C_i)) ,那么任意时刻所有优秀的物品的图像如下图所示

即随着(L) 的递增,(C) 单调递减。并且可以发现如果某个物品在某时刻不优秀,那么在之后的时刻它都不可能优秀。

考虑加入一个新的物品。

  • 这个物品本身不优秀:考虑左边与它相邻的点的纵坐标是否比它小。
  • 这个物品本身优秀:加入集合中。考虑所有在它右边的点,看是否会变得不优秀。如果是,直接删除。

具体实现可以用(multiset)

每个元素最多被加入一次,删除一次,因此总复杂度(mathcal O(nlog n))

code

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pii;
multiset<pii>s;
int n;
inline void ins(pii x)
{
	auto t=s.upper_bound(x);
	if(t==s.begin())s.insert(x);
	else
	{
		t--;auto p=*t;
		if((p.fi<x.fi&&p.se<=x.se)||p.se<x.se)return;
		s.insert(x); 
	}
	while(1)
	{
		t=s.upper_bound(x);
		if(t==s.end())break;
		if((*t).se>=x.se)s.erase(t);
		else break;
	}
}
int main()
{
	int T,kase=0;scanf("%d",&T);
	while(1)
	{
		scanf("%d",&n);
		printf("Case #%d:
",++kase);
		while(n--)
		{
			int x,y;scanf("%d%d",&x,&y);
			ins(make_pair(x,y));
			printf("%u
",s.size());
		}
		s.clear();
		if(kase<T)puts("");
		else break;
	}
	return 0;
}

异象石(SDOI2015)

description

大小为N的一棵树,M个时刻发生M个事件,类型有三:
1.某个点出现异象石。
2.删除异象石。
3.询问问异象石所在的点连通边集的总长度最小是多少。

data range

(Nle10^5)

solution

此题思路很妙啊
考虑按照(dfs) 序维护所有异象石,设(sum) 为相邻两个异象石的距离之和(首尾也算),答案就是(dfrac {sum} 2)
具体来说

  • 插入(x)

(sum) 减去(dis(pre,nxt)) ,再加上(dis(pre,x)+dis(x,nxt))

  • 删除(x)

(sum) 减去(dis(pre,x)+dis(x,nxt)) ,再加上(dis(pre,nxt))
具体维护用(set) 就可以了,但要特殊处理首尾相邻的情况
这道题还用到了不少关于(set) 的技巧,值得积累

time space complexity

时间&空间:(O(nlog n))

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,H=17;
int n,dfn[N],dfc,sign,fa[N][20],dep[N];
ll len[N];
int tin[N],tout[N];
struct edge{int to,w;edge(int _to=0,int _w=0){to=_to,w=_w;}};
struct cmp{bool operator()(const int&x,const int&y){return dfn[x]<dfn[y];}};
set<int,cmp>s;
vector<edge>e[N];
void dfs(int u,int pr)
{
	tin[u]=++sign,dfn[u]=++dfc;
	fa[u][0]=pr,dep[u]=dep[pr]+1;
	for(int i=1;(1<<i)<=dep[u];++i)
		fa[u][i]=fa[fa[u][i-1]][i-1];
	for(size_t i=0;i<e[u].size();++i)
	{
		edge v=e[u][i];
		if(v.to==pr)continue;
		len[v.to]=len[u]+1ll*v.w;
		dfs(v.to,u);
	}
	tout[u]=++sign;
}
inline bool isac(int x,int y){return tin[x]<=tin[y]&&tout[y]<=tout[x];}
inline int lca(int x,int y)
{
	if(dep[x]>dep[y])swap(x,y);
	if(isac(x,y))return x;
	for(int i=H;i>=0;--i)
		if(!isac(fa[x][i],y))
			x=fa[x][i];
	return fa[x][0];
}
inline ll dis(int x,int y)
{
	int LCA=lca(x,y);
	return len[x]+len[y]-2*len[LCA];
}
inline int pre(int x)
{
	auto it=s.lower_bound(x);
	if(it==s.begin())return *s.rbegin();
	it--;return *it;
}
inline int nxt(int x)
{
	auto it=s.lower_bound(x);
	it++;if(it==s.end())return *s.begin();
	return *it;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;++i)
	{
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		e[x].push_back(edge(y,z));
		e[y].push_back(edge(x,z));
	}
	len[1]=0;dfs(1,0);tin[0]=0,tout[0]=++sign;
	int m;scanf("%d",&m);
	char ch[5];int x;ll ans=0;
	while(m--)
	{
		scanf("%s",ch);
		if(ch[0]=='?')printf("%lld
",ans>>1);
		else
		{
			scanf("%d",&x);
			if(ch[0]=='+')
			{
				s.insert(x);
				int l=pre(x),r=nxt(x);
				ans-=dis(l,r);
				ans+=dis(l,x)+dis(x,r);
			}
			else
			{
				int l=pre(x),r=nxt(x);
				ans-=dis(l,x)+dis(x,r);
				ans+=dis(l,r);
				s.erase(x);
			}
		}
	}
	return 0;
}

图询问(Graph and Queries, Tianjin 2010, HDU3726 )

solution

删边显然难以维护。于是根据套路,将所有操作离线下来,倒序处理,就可以转删边为加边了。

计算第(k) 大权值是基础操作,不再赘述。

修改(X) 的权值为(V) 可以看作先将其原来的权值删除,再插入权值(V)

重点在于合并。这里采用启发式合并以维护复杂度。即对于两棵平衡树,我们总是会将小的合并到大的中。可以证明这样做会带来(O(log n)) 的复杂度,因为考虑每一个点,每一次合并时如果被加入了较大集合中,那么子树大小必然翻倍,而最大也就到(n) ,因此得证。

总复杂度(mathcal O(nlog^2n))

Permutation Transformer, UVa11922

solution

平衡树模板题目,主要为了熟悉代码。

code

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
mt19937 rd(time(0));
namespace fhq_Treap
{
	int rt,tot,sz[N],id[N],ls[N],rs[N],fix[N];bool tag[N];
	inline int nd(int x)
	{
		int p=++tot;id[p]=x,sz[p]=1;
		fix[p]=rd();return p;
	}
	inline void upd(int x){sz[x]=sz[ls[x]]+sz[rs[x]]+1;}
	inline void cov(int x){swap(ls[x],rs[x]);}
	inline void down(int x)
	{
		if(!tag[x])return;
		tag[ls[x]]^=1,tag[rs[x]]^=1;
		cov(ls[x]),cov(rs[x]);
		tag[x]=0;
	}
	void split(int p,int d,int&l,int&r)
	{
		if(!p){l=r=0;return;}
		down(p);
		if(sz[ls[p]]+1<=d)
		{
			l=p;
			split(rs[p],d-1-sz[ls[p]],rs[l],r);
		}
		else
		{
			r=p;
			split(ls[p],d,l,ls[r]);
		}
		upd(p);
	}
	int merge(int x,int y)
	{
		if(!x||!y)return x^y;
		down(x),down(y);
		if(fix[x]>fix[y])
		{
			rs[x]=merge(rs[x],y),upd(x);
			return x;
		}
		else
		{
			ls[y]=merge(x,ls[y]),upd(y);
			return y;
		}
	}
	inline void work(int l,int r)
	{
		int a=0,b=0,c=0;
		split(rt,l-1,a,b);
		split(b,r-l+1,b,c);
		tag[b]^=1,cov(b);
		rt=merge(merge(a,c),b);
	}
	inline void print(int p)
	{
		down(p);
		if(ls[p])print(ls[p]);
		printf("%d
",id[p]);
		if(rs[p])print(rs[p]);
	}
}
using namespace fhq_Treap;
int n,q;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)rt=merge(rt,nd(i));
	scanf("%d",&q);
	while(q--)
	{
		int l,r;scanf("%d%d",&l,&r);
		work(l,r);
	}
	print(rt);
	return 0;
}
NO PAIN NO GAIN
原文地址:https://www.cnblogs.com/zmyzmy/p/14725091.html