noip2017列队(线段树)

维护一个方阵,支持

1.删掉一个点,剩下的点先向左看齐再向前看齐

2.询问一个位置上是哪个点

$n,m,q \leq 3 \times 10^5$

sol:

我们每行前$m-1$列维护一个线段树,最后一列维护一棵线段树

然后搞n + 1个vector

这个线段树只需要维护“这个节点下面有多少点已经被删除了”

删除最后一列时,删掉一个点然后pushback即可

非最后一列时,删掉这个点,把它加到最后一列最下面,然后把本来应该在这个位置的数放到这一行最后就可以了

之前写过splay。。。线段树好写好多啊QAQ

#include
#define LL long long
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 20000005,maxm = 600010;
int n,m,q,mx;
vector vec[maxn];
int ls[maxn],rs[maxn],val[maxn],dfn,root[maxn];
inline void modify(int &x,int l,int r,int pos)
{
    if(!x)x = ++dfn;val[x]++;
    if(l == r)return;
    int mid = (l + r) >> 1;
    if(pos <= mid)modify(ls[x],l,mid,pos);
    else modify(rs[x],mid + 1,r,pos);
}
inline int query(int x,int l,int r,int pos)
{
    if(l == r)return l;
    int mid = (l + r) >> 1,sizel = mid - l + 1 - val[ls[x]];
    if(sizel >= pos)return query(ls[x],l,mid,pos);
    else return query(rs[x],mid + 1,r,pos - sizel);
}
inline LL delete_r(int x,LL v)
{
    int pos = query(root[n + 1],1,mx,x);
    modify(root[n + 1],1,mx,pos);
    LL ans = pos <= n ? 1LL * pos * m : vec[n + 1][pos - n - 1];
    vec[n + 1].push_back(v ? v : ans);
    return ans;
}
inline LL delete_l(int x,int y)
{
    int pos = query(root[x],1,mx,y);
    modify(root[x],1,mx,pos);
    LL ans = pos < m ? 1LL * (x - 1) * m + pos : vec[x][pos - m];
    vec[x].push_back(delete_r(x,ans));
    return ans;
}
int main()
{
    n = read();m = read(),q = read();
    mx = max(n,m) + q;
    while(q--)
    {
        int x = read(),y = read();
        LL ans;
        if(y == m)ans = delete_r(x,0);
        else ans = delete_l(x,y);
        printf("%lld\n",ans);
    }
}
View Code

 

转载于:https://www.cnblogs.com/Kong-Ruo/p/9754960.html

你可能感兴趣的:(noip2017列队(线段树))