zoukankan      html  css  js  c++  java
  • [NOIP2017]列队

    description

    洛谷

    solution

    一开始的想法是写(O(nlogn))拆点(Splay)——这玩意对我来说根本不可做。

    于是想了一个线段树动态开点+(Splay)的做法
    然后就调了一个晚自习,最后发现还是(O(nlog^2n))的,不开(O2)最后一个点(1975ms)

    这里是本人的解法

    首先考虑(n=1)的情况。
    此时的操作是从有序队列中取出一个节点,然后放到序列的末尾。

    对于序列末尾的元素,使用(Splay)维护位置和编号即可。
    对于不是序列末尾的元素,我们发现似乎可以使用线段树维护其编号。
    举个例子:

         a:1 2 3 4 5 6 7 8
    (1,5)->1 2 3 4[6 7 8]5
    

    我们发现除了一个被放在末尾的元素((5))以外,其余([5,7])位置的编号都(+1)
    但真的能直接这样维护吗?我们继续看:

         a:1 2 3 4 5 6 7 8
    (1,5)->1 2 3 4[6 7 8]5
    (1,3)->1 2[4 6 7 8]5 3
    

    这时我们发现(a)数组的([3,6])位置的编号有的(+1),有的(+2),
    因此直接维护编号是不可行的。

    于是我们考虑维护每个编号的元素对应出现的位置(p):

           a:1 2 3 4 5 6 7 8
           p:1 2 3 4 5 6 7 8
    (1,5)->a:1 2 3 4 6 7 8 5
           p:1 2 3 4[x 5 6 7]
    

    由于放到序列末尾的(5)已经使用了(splay)维护,因此我们不需要再维护其位置信息。
    继续看:

           a:1 2 3 4 5 6 7 8
           p:1 2 3 4 5 6 7 8
    (1,5)->a:1 2 3 4 6 7 8 5
           p:1 2 3 4[4 5 6 7]
    (1,3)->a:1 2 4 6 7 8 5 3
           p:1 2[2 3 3 4 5 6]
    

    可以发现(p)数组的维护是符合要求的。
    找到第(i)个位置的元素时,需要在(p)中二分找到第一个(==i)的元素,
    该元素的位置就是我们要找的编号。
    举个例子:

           a:1 2 3 4 5 6 7 8
           p:1 2 3 4 5 6 7 8
    (1,5)->a:1 2 3 4 6 7 8 5
           p:1 2 3 4[4 5 6 7]
    

    我们在第一次修改之后,找第(5)个位置的元素,
    那么在(p)数组中找到值为(5)的第一个元素。
    这个元素的位置为(6),因此原队列中第(5)个元素的编号是(6)
    因此相当于区间修改+单点查询

    (n)队的时候使用线段树动态开点,
    由于要在二分查找里面套线段树查询,因此线段树就是(O(nlog^2n))的了...
    还有常数大的(Splay),我都不知道怎么卡过的...

    更加优秀的(nlogn)做法

    仍然使用线段树。但线段树维护的不再是节点位置,而是区间内被删除的数的个数。
    这样我们就可以做到查询区间第(k)大了。
    具体来说,查找到一个区间时,
    左节点的数的个数相当于(左区间长度-左区间内已经被删掉的元素个数)
    而这个删除个数是可以通过动态开点来维护的。
    这里相当于单点修改+二分查找
    因此,线段树查询非询问部分编号的时间复杂度为(O(nlogn))

    我们把这种思想应用到对于询问点的处理上;
    对于后方的询问也使用动态开点线段树,插点的同时记录删除点的个数;
    同样是单点修改+二分查找,
    时间复杂度同为(O(nlogn))

    这样一来最大点就只要跑(738ms)

    Code

    本人的线段树+(Splay)做法

    #include<bits/stdc++.h>
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<iomanip>
    #include<cstring>
    #include<complex>
    #include<vector>
    #include<cstdio>
    #include<string>
    #include<bitset>
    #include<ctime>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<map>
    #include<set>
    #define Cpy(x,y) memcpy(x,y,sizeof(x))
    #define Set(x,y) memset(x,y,sizeof(x))
    #define FILE "a"
    #define mp make_pair
    #define pb push_back
    #define RG register
    #define il inline
    using namespace std;
    typedef unsigned long long ull;
    typedef vector<int>VI;
    typedef long long ll;
    typedef double dd;
    const dd eps=1e-7;
    const int N=600010;
    const int M=1000010;
    const int inf=2147483647;
    const ll INF=1e18+1;
    const ll P=100000;
    il ll read(){
    	RG ll data=0,w=1;RG char ch=getchar();
    	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    	if(ch=='-')w=-1,ch=getchar();
    	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
    	return data*w;
    }
    
    il void file(){
    	srand(time(NULL)+rand());
    	freopen(FILE".in","r",stdin);
    	freopen(FILE".out","w",stdout);
    }
    
    /*********************************************************************/
    
    int n,m,q;
    int cnt,srt[N],ss[2][20*N],slz[20*N];
    #define mid ((l+r)>>1)
    void mod(int &i,int l,int r,int x,int y,int d){
    	if(x>y)return;if(!i)i=++cnt;
    	if(x<=l&&r<=y){slz[i]+=d;return;}
    	if(x<=mid)mod(ss[0][i],l,mid,x,y,d);
    	if(mid<y)mod(ss[1][i],mid+1,r,x,y,d);
    }
    int qry(int i,int l,int r,int p){
    	if(!i||l==r)return slz[i];
    	if(p<=mid)return slz[i]+qry(ss[0][i],l,mid,p);
    	else return slz[i]+qry(ss[1][i],mid+1,r,p);
    }
    /*********************************************************************/
    
    int rt[N],s[2][N],fa[N],sz[N];ll rk[N];int tot;
    int cal[N],top;
    int newnode(){return top?cal[top--]:++tot;}
    
    #define isr(i) (s[1][fa[i]]==i)
    il void update(int i){if(i)sz[i]=sz[s[0][i]]+sz[s[1][i]]+1;}
    il void rot(int i){
    	RG int j=fa[i],k=fa[j];RG bool b=isr(i);
    	if(k)s[isr(j)][k]=i;fa[i]=k;
    	if(s[!b][i])fa[s[!b][i]]=j;s[b][j]=s[!b][i];
    	s[!b][i]=j;if(j)fa[j]=i;update(j);
    }
    il void splay(int i,int a,int k){
    	for(RG int j=fa[i];j!=a;rot(i),j=fa[i]){
    		if(fa[j])isr(i)^isr(j)?rot(i):rot(j);
    		if(fa[i]==a)break;
    	}
    	if(!a)rt[k]=i;update(i);
    }
    il ll find(int k,int p){
    	RG int i=rt[k],pre;
    	while(2333){
    		if(!i)break;
    		if(p<=sz[s[0][i]])i=s[0][i];
    		else if(p==sz[s[0][i]]+1){
    			splay(i,0,k);
    			pre=s[0][i];while(s[1][pre])pre=s[1][pre];
    			if(pre){
    				splay(pre,i,k);rt[k]=pre;
    				fa[s[1][i]]=pre;s[1][pre]=s[1][i];
    				s[0][i]=s[1][i]=fa[pre]=0;update(pre);
    			}
    			else{
    				rt[k]=s[1][i];fa[s[1][i]]=0;s[1][i]=0;
    			}
    			cal[++top]=i;return rk[i];
    		}
    		else p-=sz[s[0][i]]+1,i=s[1][i];
    	}
    }
    il void insert(int k,ll r){
    	RG int i=rt[k],nw;
    	while(s[1][i])i=s[1][i];
    	if(!i)rt[k]=nw=newnode();
    	else splay(i,0,k),s[1][i]=nw=newnode();
    	fa[nw]=i;sz[nw]=1;rk[nw]=r;s[0][nw]=s[1][nw]=0;
    	update(i);
    }
    /*********************************************************************/
    
    int sq[N];
    il int lowerbound(int k,int p){
    	RG int l=1,r=(k==n+1?n:m-1),len=r,as;
    	while(l<r){
    		as=(mid+qry(srt[k],1,len,mid));
    		if(as<p)l=mid+1;else r=mid;
    	}
    	return l;
    }
    il ll query(int k,int p){
    	RG int len=(k==n+1?n:m-1);ll r;
    	if(p>len-sq[k])r=find(k,p+sq[k]-len);
    	else{
    		sq[k]++;
    		if(k==n+1){
    			r=lowerbound(k,p);
    			mod(srt[k],1,n,r,n,-1);
    			r=r*m;
    		}
    		else{
    			r=lowerbound(k,p);
    			mod(srt[k],1,m-1,r,m-1,-1);
    			r=1ll*(k-1)*m+r;
    		}
    	}
    	return r;
    }
    /*********************************************************************/
    
    int main()
    {
    	n=read();m=read();q=read();
    	for(RG int i=1,x,y;i<=q;i++){
    		RG ll r;
    		x=read();y=read();
    		
    		r=query(n+1,x);
    		if(y!=m){
    			insert(x,r);
    			r=query(x,y);
    		}
    		insert(n+1,r);
    		printf("%lld
    ",r);
    	}
    	return 0;
    }
    
    

    更好的线段树做法

    #include<bits/stdc++.h>
    #define RG register
    #define il inline
    using namespace std;
    typedef long long ll;
    const int N=300010;
    il ll read(){
    	RG ll data=0,w=1;RG char ch=getchar();
    	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    	if(ch=='-')w=-1,ch=getchar();
    	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
    	return data*w;
    }
    int n,m,q,x,y,sz[N];
    int rt[2][N],s[2][40*N],sum[40*N],tot;ll id[40*N];
    #define mid ((l+r)>>1)
    ll query(int &i,int l,int r,int p,int k){
    	if(!i)i=++tot;sum[i]++;if(l==r)return k?id[i]:l;
    	if(p<=(mid-l+1)-sum[s[0][i]])return query(s[0][i],l,mid,p,k);
    	else return query(s[1][i],mid+1,r,p-((mid-l+1)-sum[s[0][i]]),k);
    }
    void insert(int &i,int l,int r,int p,ll v){
    	if(!i)i=++tot;if(l==r){id[tot]=v;return;}
    	if(p<=(mid-l+1)-sum[s[0][i]])insert(s[0][i],l,mid,p,v);
    	else insert(s[1][i],mid+1,r,p-((mid-l+1)-sum[s[0][i]]),v);
    }
    il ll find(int k,int len,int p){
    	if(p>len-sz[k]){return query(rt[1][k],1,q,p+(sz[k]--)-len,1);}
    	if(k==n+1)return 1ll*m*query(rt[0][k],1,len,p,0);
    	else return 1ll*(k-1)*m+query(rt[0][k],1,len,p,0);
    }
    ll work(int x,int y){
    	RG ll r1=find(n+1,n,x),r2;
    	if(y!=m){
    		r2=find(x,m-1,y);
    		sz[x]++;
    		insert(rt[1][x],1,q,sz[x],r1);
    		swap(r1,r2);
    	}
    	sz[n+1]++;
    	insert(rt[1][n+1],1,q,sz[n+1],r1);
    	return r1;
    }
    int main()
    {
    	n=read();m=read();q=read();
    	for(RG int i=1;i<=q;i++){
    		x=read();y=read();
    		printf("%lld
    ",work(x,y));
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    WebDriverException: Message: 'geckodriver' executable needs to be in PATH.
    Django安装与使用
    初识Django
    python学习之xlrd的使用
    python 学习笔记
    根据当前日期生成一个唯一标识的名称
    用Python生成组织机构代码,附源码
    IO流基础
    多线程
    日期时间类
  • 原文地址:https://www.cnblogs.com/cjfdf/p/9686823.html
Copyright © 2011-2022 走看看