zoukankan      html  css  js  c++  java
  • 洛谷.3960.列队(线段树/树状数组)

    洛谷
    UOJ 334

    线段树做法:
    先考虑一行的情况。每次操作为在序列中取出一个元素,然后放到取完后的第n个位置。如果我们预留n+q个位置,那这个操作就相当于线段树单点删除和单点加入。
    扩展到多行。我们发现对最后一列的操作和对某一行的操作是相同的(取出一个,放到末尾一个)。
    于是我们可以用n+1棵动态开点线段树维护。
    有些细节:比如删除后不需要真插到线段树里。。用vector即可。

    树状数组做法:
    基本思路不变(一维扩展到多维,最后一列单独维护)。
    对于每一行(除最后一列),有影响的只是对这一行的操作。
    那么对于某一行,可以算出某次操作删掉的是第几次补在这一行后的数(或者是原数列中的哪个数)。同样是查k大值计算。
    因为总共只会有q个补在某行后面的数,可以直接用vector存每一行每次补在后面的数。最后一列一样。
    求k大值(第k个存在的数)线段树平衡树都行。二分+树状数组也可以。
    因为不能动态开点开n+1棵树状数组,所以离线,每次对每一行用树状数组求,然后改初始状态。
    最后对最后一列也求一遍即可。
    复杂度(O(nlog^2n))。但常数小啊。(但还是比线段树慢?)
    我竟然用边表存每一行的询问mdzz(顺序会反)。

    至于平衡树。。真心不想再写它了。

    线段树:

    //1736ms	54284kb
    #include <cstdio>
    #include <cctype>
    #include <vector>
    #include <algorithm>
    //#define gc() getchar()
    #define MAXIN 150000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    typedef long long LL;
    const int N=3e5+4;
    
    int n,m,R,root[N];
    std::vector<LL> v[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    struct Segment_Tree
    {
    	#define S N*18//*2
    	#define lson son[x][0],l,m
    	#define rson son[x][1],m+1,r
    	int tot,son[S][2],sum[S];//不需要直接记区间点数啊== 记删除点数就行 
    	#undef S
    
    	int Delete(int &x,int l,int r,int p)
    	{
    		if(!x) x=++tot; ++sum[x];
    		if(l==r) return l;
    		int m=l+r>>1,tmp=m-l+1-sum[son[x][0]];
    		if(tmp<p) return Delete(rson,p-tmp);
    		return Delete(lson,p);
    	}
    }T;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    LL Solve1(int x,LL tmp)
    {
    	int p=T.Delete(root[0],1,R,x);
    	LL ans= p<=n?(LL)p*m:v[0][p-n-1];
    	v[0].push_back(tmp?tmp:ans);
    	return ans;
    }
    LL Solve2(int x,int y)
    {
    	int p=T.Delete(root[x],1,R,y);
    	LL ans= p<m?(1ll*(x-1)*m+p):v[x][p-m];
    	v[x].push_back(Solve1(x,ans));
    	return ans;
    }
    
    int main()
    {
    	n=read(),m=read(); int Q=read(); R=std::max(n,m)+Q;
    	for(int x,y; Q--; )
    	{
    		x=read(),y=read();
    		if(y==m) printf("%lld
    ",Solve1(x,0));
    		else printf("%lld
    ",Solve2(x,y));
    	}
    	return 0;
    }
    

    树状数组:

    //1951ms	32664kb
    #include <cstdio>
    #include <cctype>
    #include <vector>
    #include <algorithm>
    //#define gc() getchar()
    #define MAXIN 300000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    typedef long long LL;
    const int N=3e5+5;
    
    int qx[N],qy[N],pos[N];
    std::vector<LL> e[N],v[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    struct BIT
    {
    	int n,t[N*4];//2n! 再开一倍因为预处理 
    	#define lb(x) (x&-x)
    	inline void Add(int p)
    	{
    		for(; p<=n; p+=lb(p)) ++t[p];
    	}
    	inline void Delete(int p)
    	{
    		for(; p<=n; p+=lb(p)) --t[p];
    	}
    	inline int Query(int p)
    	{
    		int res=0;
    		for(; p; p^=lb(p)) res+=t[p];
    		return res;
    	}
    	void Init(int nn)
    	{
    		n=nn, ++t[n];
    		for(int i=1; i<n; ++i) t[i+lb(i)]+=++t[i];//init:t[i]=1 i+lb(i)>n
    	}
    	inline int Kth(int k)
    	{
    		int l=1,r=n,mid;
    		while(l<r)
    			if(Query(mid=l+r>>1)<k) l=mid+1;
    			else r=mid;
    		return l;
    	}
    }T;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    
    int main()
    {
    	int n=read(),m=read(),q=read();
    	for(int i=1; i<=q; ++i)
    	{
    		qx[i]=read(),qy[i]=read();
    		if(qy[i]!=m) e[qx[i]].push_back(i);
    	}
    
    	T.Init(std::max(n,m)+q);
    	for(int x=1; x<=n; ++x)
    	{
    		for(int i=0,j,l=e[x].size(); i<l; ++i)
    			j=e[x][i], T.Delete(pos[j]=T.Kth(qy[j]));
    		for(int i=0,l=e[x].size(); i<l; ++i) T.Add(pos[e[x][i]]);
    	}
    	for(int i=1,x,y,p; i<=q; ++i)
    	{
    		x=qx[i],y=qy[i];
    		T.Delete(p=T.Kth(x));
    		LL ans= p<=n?(LL)p*m:v[0][p-n-1];
    		if(y!=m)
    		{
    			v[x].push_back(ans);
    			ans= pos[i]<m?(1ll*(x-1)*m+pos[i]):v[x][pos[i]-m];
    		}
    		v[0].push_back(ans);
    		printf("%lld
    ",ans);
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    外观模式
    享元模式
    装饰模式
    适配器模式
    组合模式
    典型用户模板与场景
    知识圈APP开发记录(十二)
    知识圈APP开发记录(十一)
    知识圈APP开发记录(十)
    周总结(七)
  • 原文地址:https://www.cnblogs.com/SovietPower/p/9750222.html
Copyright © 2011-2022 走看看