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

    题解[NOIP2017] 列队

    题面

    解析

    看到这题时感觉这个编号很难维护啊?

    后来看了lzf大佬的题解才会..

    首先,考虑一个稍微暴力的做法,

    维护每一行的前(m-1)个人和最后一列的(n)个人的编号,

    也就是用(n+1)个区间分开维护

    设当前询问((x,y)),

    那么就在第(x)行中把它删掉,

    再把最后一列的对应第(x)行的人加入第(x)行中,

    最后将询问的人加入最后一列.

    (当然如果询问的人在最后一列要特判)

    但显然这样时间和空间都会炸...

    于是考虑优化,

    首先意识到只有(q)个询问,即每个区间长度最多只会加(q)

    那么可以考虑对每个区间用一棵类似于权值线段树的结构动态开点,

    具体来说,拿一个区间举个例子,

    设区间长度为(l),

    那么我们开一棵区间总长度为(l+q)的权值线段树,

    其中前(l)个是本来就在那里的,后面的是后来加进来的,

    显然前(l)个的编号就是它本来的编号规则,而后面的可以用一个(vector)维护,

    这样空间复杂度也就下来了.

    每个节点维护一个(cnt)表示它代表的区间里有多少个元素被删(出队)了,

    那么区间长度-(cnt)就是区间里还剩下的人数,

    而询问其实也就是找剩下的第(y)个人.

    和左子树的剩下的人数比大小就行了.

    最后还是注意如果询问在最后一列要特判.

    code:

    
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #define int long long
    #define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
    using namespace std;
    
    inline int read(){
    	int sum=0,f=1;char ch=getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    	return f*sum;
    }
    
    const int N=1000005;
    struct tree{int cnt,l,r;}t[N<<2];
    int n,m,Q,rt[N],tot;
    vector<int> v[N];
    
    inline int dinf(int &p,int l,int r,int x){
    	if(!p) p=++tot,t[p].cnt=0;
    	t[p].cnt++;
    	if(l==r) return l;
    	int mid=(l+r)>>1;
    	int cc=mid-l+1-t[t[p].l].cnt;
    	if(cc>=x) return dinf(t[p].l,l,mid,x);
    	else return dinf(t[p].r,mid+1,r,x-cc);
    }
    
    signed main(){
    	n=read();m=read();Q=read();
    	int l1=m-1+Q,l2=n+Q;
    	while(Q--){
    		int x=read(),y=read();
    		if(y<m){
    			int pos=dinf(rt[x],1,l1,y);
    			int ans=pos<m? (x-1)*m+pos:v[x][pos-m];
    			v[n+1].push_back(ans);
    			int pos1=dinf(rt[n+1],1,l2,x);
    			int ans1=pos1<=n? pos1*m:v[n+1][pos1-n-1];
    			v[x].push_back(ans1);
    			printf("%lld
    ",ans);
    		}
    		else{
    			int pos=dinf(rt[n+1],1,l2,x);
    			int ans=pos<=n? pos*m:v[n+1][pos-n-1];
    			v[n+1].push_back(ans);
    			printf("%lld
    ",ans);
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    NOI2013 树的计数
    拆系数FFT学习笔记
    HAOI2017 八纵八横——线段树分治+线性基
    BJOI2018链上二次求和——线段树
    [bzoj3514]Codechef MARCH14 GERALD07加强版——lct+主席树
    KD-Tree 学习笔记
    SDOI2010 捉迷藏 —— KD-Tree
    HAOI2018染色——容斥
    gitignore 不起作用的解决办法
    reids学习教程
  • 原文地址:https://www.cnblogs.com/zsq259/p/11829590.html
Copyright © 2011-2022 走看看