洛谷.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;
}
原文地址:https://www.cnblogs.com/SovietPower/p/9750222.html