zoukankan      html  css  js  c++  java
  • 【NOIP2017提高组正式赛】列队

    题解

    本题的解法是丰富多彩的!
    线段树做法是极好的
    代码非常之少

    一个很显然的想法是维护 (n+1) 颗线段树
    那要怎么维护才能不爆空间呢?
    我们发现尽管 (n imes m) 那么大
    (q) 相比 (n imes m) 小得多
    也就是说变动的点比较少
    于是我们抓住这点
    考虑只记录变动的点
    行的线段树维护 ([1,m-1]) 位置的信息
    列的线段树维护 ([1,n]) 的位置的信息

    那我一颗线段树就记录那些点不在原来位置了
    也就是说一颗线段树长为 (max(n,m)+q) (为什么?待会再解释)
    每一位只为 (0)(1) 表示 没变或变了
    (1) 表示变了是因为没变的太多无法记录,那就记变的吧
    这样线段树动态开点空间就可以接受了
    然后变动的点我们考虑它后来的位置
    依题知它会到最后一列的末端,然后最后一列它所在的行的点会到它所在行的线段是的末端
    我们无法向平衡树一样动态移动(插入,删除)点,但题目只需插入到末端
    所以我们预留空间把插入的点放到末端,然后它原来位置标记为 (1),表示此处无点
    对于查询,我们知道为第 (x(x<=n)) 颗线段树的 (y) 位置的点的编号为 ((x-1) imes m+y)

    但他不一定在原队列 (y) 位置
    因为前面有些点为 (1) 空置,一个区间长度减去区间 (1) 的个数才是区间有的数
    所以我们只要查询排名为 (y) 的位置的编号,记为 (z)
    那么在询问中,当 (z<m) 时,它的编号为 ((x-1) imes m+z)
    (z >= m) 时说明前面有空位,有点移动到后面了
    我们用 (n+1)(vector) 储存每个线段树依次移动到后面的点,输出 (S[x][z-m]) 即可
    列同理

    (Code)

    #include<cstdio>
    #include<vector>
    #include<iostream>
    #define LL long long
    using namespace std;
    
    const int N = 3e5 + 5, M = 6e6 + 5;
    int n, m, q, sum[M], ls[M], rs[M], rt[N], Len, size;
    vector<LL> S[N];
    
    void update(int &p, int l, int r, int x)
    {
    	if (!p) p = ++size;
    	++sum[p];
    	if (l == r) return;
    	int mid = (l + r) >> 1;
    	if (x <= mid) update(ls[p], l, mid, x);
    	else update(rs[p], mid + 1, r, x);
    }
    
    int query(int p, int l, int r, int k)
    {
    	if (l == r) return l;
    	int mid = (l + r) >> 1, s = mid - l + 1 - sum[ls[p]];
    	if (k <= s) return query(ls[p], l, mid, k);
    	return query(rs[p], mid + 1, r, k - s);
    }
    
    inline LL move_forward(int x)
    {
    	int p = query(rt[n + 1], 1, Len, x);
    	update(rt[n + 1], 1, Len, p);
    	return (p <= n ? (1LL * p * m) : (S[n + 1][p - n - 1]));
    }
    
    inline LL move_left(int x, int y)
    {
    	int p = query(rt[x], 1, Len, y);
    	update(rt[x], 1, Len, p);
    	return (p < m ? (1LL * (x - 1) * m + p) : (S[x][p - m]));
    }
    
    int main()
    {
    	freopen("phalanx.in", "r", stdin);
    	freopen("phalanx.out", "w", stdout);
    	scanf("%d%d%d", &n, &m, &q);
    	Len = max(n, m) + q;
    	for(int x, y; q; --q)
    	{
    		scanf("%d%d", &x, &y);
    		LL z;
    		if (y == m) z = move_forward(x), printf("%lld
    ", z), S[n + 1].push_back(z);
    		else z = move_left(x, y), printf("%lld
    ", z), S[n + 1].push_back(z),
    			z = move_forward(x), S[x].push_back(z);
    	}
    }
    
  • 相关阅读:
    标准I/O的缓冲
    Linux 的文件类型
    引用和指针
    信号-总结
    实时信号
    信号的其它特性
    信号处理器函数
    信号集 / 信号掩码(阻塞信号传递)
    显示信号描述
    发送信号
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/14311399.html
Copyright © 2011-2022 走看看