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

    [NOIP2017 提高组] 列队

    (n imes m) 的方阵, (k) 次询问,每次从其中取走一个人后向上向左重整队伍,询问取走的人是谁

    (n,m,qleq3 imes10^5)

    很好的一道动态开点线段树题

    考虑一下,每次取走一个人会发生什么:

    (17) 所在的行,右边的部分全都向左移动一个; 第 (m) 列,从 (17) 所在的列开始全部向上一个。

    发现每一行之间都是独立的(除了最后一列)。

    这样我们就可以把这个矩阵分割成如下的几块:

    这些部分互相是独立的,可以分别用数据结构来维护。

    我们再考虑一下这个数据结构内需要包含什么东西。

    • 操作:查询 ((x,y)) 的元素
    • 操作:向末尾插入元素

    因为我们肯定不能把所有元素都插入到这个数据结构内,所以我们考虑动态开点线段树

    不同于一般的动态开点,这里选择在查询/插入的过程中动态开点以取代低效的建树过程。

    具体而言,在查询/插入的时候,直接动态开点,如果它尚无儿子就新建儿子节点。

    注意判断,什么时候节点的 (sum) 有值。

    #include <bits/stdc++.h>
    #define fo(a) freopen(a".in","r",stdin), freopen(a".out","w",stdout)
    using namespace std;
    const int INF = 0x3f3f3f3f, N = 6e5+5;
    typedef long long ll;
    typedef unsigned long long ull;
    inline ll read(){
    	ll ret = 0; char ch = ' ', c = getchar();
    	while(!(c >= '0' && c <= '9')) ch = c, c = getchar();
    	while(c >= '0' && c <= '9') ret = (ret << 1) + (ret << 3) + c - '0', c = getchar();
    	return ch == '-' ? -ret : ret;
    }
    int n,m,q;
    struct Segtre{
    	int ls,rs,sum; ll val;
    }tr[N<<5];
    int tot;
    inline int getsum(int id,int l,int r){
    	if(id <= n){
    		r = min(r,m-1);
    		return max(0,r-l+1);
    	}
    	r = min(r,n);
    	return max(0,r-l+1);
    }
    inline ll getval(ll id,ll l){
    	if(id <= n) return (id-1) * m + l;
    	else return l*m;
    }
    inline void pushup(int k){tr[k].sum = tr[tr[k].ls].sum + tr[tr[k].rs].sum;}
    ll query(ll id,int k,int l,int r,int x){
    	if(l == r)
    		return tr[k].sum = 0, tr[k].val;
    	int mid = (l + r) >> 1;
    	if(!tr[k].ls){
    		tr[k].ls = ++tot;
    		tr[tr[k].ls].sum = getsum(id,l,mid);
    		if(l == mid) tr[tr[k].ls].val = getval(id,l);
    	}
    	if(!tr[k].rs){
    		tr[k].rs = ++tot;
    		tr[tr[k].rs].sum = getsum(id,mid+1,r);
    		if(r == mid+1) tr[tr[k].rs].val = getval(id,r);
    	}
    	ll ret = 0;
    	if(x <= tr[tr[k].ls].sum) ret = query(id,tr[k].ls,l,mid,x);
    	else ret = query(id,tr[k].rs,mid+1,r,x-tr[tr[k].ls].sum);
    	pushup(k);
    	return ret;
    }
    void insert(ll id,int k,int l,int r,int x,ll w){
    	if(l == r)
    		return tr[k].sum = 1, void(tr[k].val = w);
    	int mid = (l + r) >> 1;
    	if(!tr[k].ls){
    		tr[k].ls = ++tot;
    		tr[tr[k].ls].sum = getsum(id,l,mid);
    		if(l == mid) tr[tr[k].ls].val = getval(id,l);
    	}
    	if(!tr[k].rs){
    		tr[k].rs = ++tot;
    		tr[tr[k].rs].sum = getsum(id,mid+1,r);
    		if(r == mid+1) tr[tr[k].rs].val = getval(id,r);
    	}
    	if(x <= mid) insert(id,tr[k].ls,l,mid,x,w);
    	else insert(id,tr[k].rs,mid+1,r,x,w);
    	pushup(k);
    }
    signed main(){
    //	printf("%.2lf",1.0*sizeof(tr)/1024/1024);
    	n = read(), m = read(), q = read();
    	tot = n+1; const int siz = max(n,m-1)+q, mx = max(n,m-1);
    	for(int i = 1 ; i <= q ; i ++){
    		int x = read(), y = read();
    		if(y < m){
    			ll out = query(x,x,1,siz,y);
    			printf("%lld
    ",out);
    			ll in = query(n+1,n+1,1,siz,x);
    //			printf(" PUSHIN(%d)
    ",in);
    			insert(x,x,1,siz,mx+i,in);
    			insert(n+1,n+1,1,siz,mx+i,out);
    		}
    		else{
    			ll out = query(n+1,n+1,1,siz,x);
    			printf("%lld
    ",out);
    			insert(n+1,n+1,1,siz,mx+i,out);
    		}
    	}
    }
    
  • 相关阅读:
    OO设计精要:封装,还是封装(有感于“Why getter and setter methods are evil ”by Allen Holub )
    博客园建议:能否记住在博客园的首页上只显示标题
    戴尔国际英语
    C#代码契约(转)
    C#数组传递和返回
    SecureString
    里氏替换原则
    ASP.NET的Cache(转)
    WCF服务
    C#枚举中的位运算权限分配
  • 原文地址:https://www.cnblogs.com/Shinomiya/p/15549476.html
Copyright © 2011-2022 走看看