NOIP2017 phalanx 队列

题目

有一个n*m的方阵,一开始,格子(i,j)上的数是(i-1)*m+j
有q次操作,每次给定 ( x , y ) (x,y) (x,y),将格子(x,y)中的数取出,第x行的数向左补空,然后第m列的数向前补空。
50分,q<=500,n,m<=50000
另30分,x=1
100分,n,m,q<=300000

题解

部分分?
50分:用vector。
由于q<=500,所以涉及到换位置的数不超过500*50000个。
时间复杂度: O ( q n ∗ l o g   n ) O(qn*log\ n) O(qnlog n)
开50000个vector。
v[i]表示第i行的数。
显然,将(x,y)取出后,(x,y)左边的数是不会动的。
所以,如果对于第x行,y是这么多次操作中最小的,那么将没有动过的那些数也加入vector。
做完这步之后,就只剩下交换操作了。
80分:x=1
直接线段树。维护sum和标记。
不要开小数组!!
100分:结合2种做法:
开300001(n+1)个线段树,每个线段树维护着一些连续的区间。
显然,每个线段树开始只有1个点。
进行一次操作。如果y<m,那么有2个线段树将分裂区间。
否则,代表最后一列的那棵线段树将分裂区间。
同样地,要维护标记。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 300010
#define LL long long
#define P(a) putchar(a)
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note{
	int sum,ls,rs;
	LL bj;
};note tr[N*40];
int i,j,k,l;
LL n,m,q,q1,wz;
int u,v;
int tot;
int root[N],tail[N];
LL ans,temp;
int read(){
	int res=0,fh=1;char ch;
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')fh=-1,ch=getchar();
	while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar();
	return res*fh;
}
void write(LL x){
	if(x>9)write(x/10);
	P(x%10+'0');
}
void update(int ps){
	tr[ps].sum=tr[tr[ps].ls].sum+tr[tr[ps].rs].sum;
}
LL find(int &ps,int l,int r,LL x){
	if(!ps)ps=++tot;
	if(l==r){
		wz=l;
		tr[ps].sum=1;
		return tr[ps].bj;
	}
	int mid=(l+r)>>1;
	LL res;
	if(x>mid-l+1-tr[tr[ps].ls].sum)
	    res=find(tr[ps].rs,mid+1,r,x-(mid-l+1-tr[tr[ps].ls].sum));
	    else res=find(tr[ps].ls,l,mid,x);
	update(ps);
	return res;
}
void change(int &ps,int l,int r,int x,LL z){
	if(!ps)ps=++tot;
	if(l==r){
		tr[ps].bj=z;
	    return; 
	}
	int mid=(l+r)>>1;
	if(x<=mid)change(tr[ps].ls,l,mid,x,z);
	     else change(tr[ps].rs,mid+1,r,x,z); 
	update(ps);
}
int main(){
    cin>>n>>m>>q;
	tail[n+1]=n;
	fo(i,1,n)tail[i]=m-1;
	q1=q;
	while(q1--){
		ans=0;
		u=read(),v=read();
		if(v==m){
			wz=0;
			ans=find(root[n+1],1,n+q,u);
			if(wz<=n)ans=1ll*wz*m;
			change(root[n+1],1,n+q,++tail[n+1],ans);
		} else{
			wz=0;
			temp=find(root[u],1,m+q,v);
			if(wz<=m-1)ans=1ll*(u-1)*m+1ll*wz;else ans=temp;
			change(root[n+1],1,n+q,++tail[n+1],ans);
			wz=0;
			temp=find(root[n+1],1,n+q,u);
			if(wz<=n)temp=1ll*wz*m;
			change(root[u],1,m+q,++tail[u],temp);
		}
		write(ans),P('\n');
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值