• 平衡树


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

    优势人群(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
  • 相关阅读:
    真正的e时代
    在线手册
    UVA 10616 Divisible Group Sums
    UVA 10721 Bar Codes
    UVA 10205 Stack 'em Up
    UVA 10247 Complete Tree Labeling
    UVA 10081 Tight Words
    UVA 11125 Arrange Some Marbles
    UVA 10128 Queue
    UVA 10912 Simple Minded Hashing
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/14725091.html
Copyright © 2020-2023  润新知