zoukankan      html  css  js  c++  java
  • 平衡树

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

    优势人群(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
  • 相关阅读:
    算法导论13:双向循环链表 2016.1.13
    ansible
    mariadb集群配置(主从和多主)
    连接查询,视图,事物,索引,外键(第四章)
    MariaDB第三章(select)
    mariadb第二章-增删改
    mariadb(第一章)
    keepalived概述
    git操作
    django后台admin管理布局
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/14725091.html
Copyright © 2011-2022 走看看