洛谷 P3960 列队【线段树】

用动态开点线段树分别维护每一行和最后一列,线段树的作用是记录被选的点的个数以及查询第k个没被选的点,每次修改,从行里标记被选的点,从最后一列标记向左看齐之后少的点,然后用vector维护行列的新增点

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int N=600005;
int n,m,q,mx,tot,rt[N];
vector<long long>a[N];
struct qwe
{
	int ls,rs,s;
}t[10000005];
int read()
{
	int r=0,f=1;
	char p=getchar();
	while(p>'9'||p<'0')
	{
		if(p=='-')
			f=-1;
		p=getchar();
	}
	while(p>='0'&&p<='9')
	{
		r=r*10+p-48;
		p=getchar();
	}
	return r*f;
}
void update(int &ro,int l,int r,int p)
{
	if(!ro)
		ro=++tot;
	t[ro].s++;
	if(l==r)
		return;
	int mid=(l+r)>>1;
	if(p<=mid)
		update(t[ro].ls,l,mid,p);
	else
		update(t[ro].rs,mid+1,r,p);
}
int ques(int ro,int l,int r,int k)
{
	if(l==r)
		return l;
	int mid=(l+r)>>1;
	if(mid-l+1-t[t[ro].ls].s>=k)
		return ques(t[ro].ls,l,mid,k);
	else
		return ques(t[ro].rs,mid+1,r,k-(mid-l+1-t[t[ro].ls].s));
}
int main()
{
	n=read(),m=read(),q=read(),mx=max(m,n)+q;
	while(q--)
	{
		int x=read(),y=read();
		if(y==m)
		{
			int p=ques(rt[n+1],1,mx,x);
			update(rt[n+1],1,mx,p);
			long long ans=p<=n?1ll*p*m:a[n+1][p-n-1];
			a[n+1].push_back(ans);
			printf("%lld\n",ans);
		}
		else
		{
			int p=ques(rt[x],1,mx,y);
			update(rt[x],1,mx,p);;
			long long ans=p<m?1ll*(x-1)*m+p:a[x][p-m];
			p=ques(rt[n+1],1,mx,x);
			update(rt[n+1],1,mx,p);
			long long ans2=p<=n?1ll*p*m:a[n+1][p-n-1];
			a[n+1].push_back(ans);
			a[x].push_back(ans2);
			printf("%lld\n",ans);
		}
	}
	return 0;
}
posted @ 2018-09-26 21:10  lokiii  阅读(106)  评论(0编辑  收藏  举报