2017 noip 列队(动态开节点)

每行用一个线段树来维护在这行的人的信息,最后一列用一个线段树在这行的人的信息。

一个人离队。

如果这个人(代号人1)不在最后一列,出队,就把他的信息从他所在的行所代表的线段树中删除,再把在这一行最后一列的人(代号人2)的信息从最后一列删除,再把人2的信息加入到这一行的的线段树中。最后把人1的信息加入到最后一列所在的线段树的末尾。

如果这个人在最后一列,把他他的信息从线段树里删除,再加到最后一列所在的线段树的末尾

然而显然爆空间,所以考虑动态开节点。//要要到这个点的信息时才开。

线段树上每个节点维护这个点所代表的区间有多少个人-。-

叶子节点还要维护在站在这个点的人的编号。

每棵线段树的大小为:

每行的线段树:m+q

最后一列的线段树n+q;

//pos表示的含义,query中pos是它在这一行或列中是第几个,modify中pos为它加的位置的下标。

query,modify如果访问到没访问的节点,则新开一个节点,处理出这个区间有多少个人(如果第一次肯定没有删除过所以可以用节点所代表的l,r来处理出),如果是叶子节点还要处理编号(用一个hs代表这个人是在行所代表的线段树还是在列所代表的线段树)。

我的query顺便带了删除,其实写到modify里也可以,但是这样写可以降低复杂度.

#include<iostream>
#include<cstdio>
using namespace std;
int n, m, q, tp,  end[6000004],  rt[6000004], ls[6000004], rs[6000004];
long long tot,siz[6000004], val[6000004];
void read(int &x)
{
	x = 0;char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9')
	{
		x = 10 * x + c - '0';
		c = getchar();
	}
}
int suan(int l,int r,int hs)
{
	if(hs == n + 1)
	{
			if(r <= n)
			{
				return r - l +1;
			}
			else if(l <= n) return n - l +1;
			else return 0;
	}
	else
    {
    	if(r <= m-1)
    	{
    		return r - l +1;
    	}
    	else if(l <= m-1) return m - l;
    	else return 0;
    }
}
long long qurey(int &o,int l,int r,int pos,int hs)
{
	if(o == 0)
	{
		o = ++tot;
		siz[o] = suan(l,r,hs);
	    	if( l == r)
 	        {
		    if(hs == n+1)val[o] = 1ll * m * l;
		    else val[o] = 1ll * m * (hs-1) + 1ll * l;
	        }  
	}
	siz[o]--;
	if(l == r) return val[o];
    int mid = (l + r) / 2;
	if(siz[ls[o]] >= pos || (ls[o] == 0 && (mid-l+1) >= pos)) return qurey(ls[o],l, mid, pos,hs);
    else
	{
		if(ls[o]) pos -= siz[ls[o]]; else pos -= (mid - l +1);
		return qurey(rs[o],mid+1, r,pos,hs);
    }
}
void modify(int &o,int l,int r,int pos, long long va,int hs)
{
	if(o == 0)
	{
		o = ++ tot;
	   siz[o] = suan(l,r,hs);
	    if( l == r)  val[o] = va;
	}
	siz[o]++;
	if(l == r) return ;
    int mid = (l + r) /2;
	if(mid >= pos) modify(ls[o],l,mid,pos,va,hs);
	else modify(rs[o],mid+1,r,pos,va,hs);
}
int main()
{
	//freopen("ha.in","r",stdin);
	//freopen("ha.out","w",stdout);
	read(n);  read(m); read(q);
	for(int i = 1; i <= n; i++)
	{
	 	rt[i] = ++tot; end[i] = m - 1;
	}
	rt[n+1] = ++tot;   end[n + 1] = n;
	for(int i = 1; i <= q; i++)
	{
	long long x,y;  scanf("%lld",&x);
	scanf("%lld",&y);
		if(y == m)
		{
          long long ha = qurey(rt[n+1],1,n+q,x,n+1);
          end[n+1]++;
          printf("%lld\n",ha);
          modify(rt[n+1],1,n+q,end[n+1],ha,n+1);
          continue;
		}
		long long ha = qurey(rt[n+1], 1, n+q, x, n+1);
		long long he = qurey(rt[x], 1, m-1+q, y, x);
		 printf("%lld\n",he);
		end[x]++; end[n+1]++;
		modify(rt[x], 1, m-1+q, end[x], ha, x);
	    modify(rt[n+1], 1, n+q, end[n+1],he,1 + n);
	}
	 return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值