NOIP2017d2t3 列队 动态开点线段树

题意

有一个n*m的矩阵,一开始位置(x,y)上的元素为(x-1)*m+y。接下来有q个操作:每次操作为(x,y),表示先输出位置(x,y)上的值,设t为(x,y)上的值,然后把第x行[y+1…m]上的每个数往前移一位,把最后一列[x+1…n]上的每个数往前移一位,最后把t放到位置(n,m)。
n,m,q<=300000

分析

考场上大概想到了做法,但由于一个地方把n和m打反而少了35分,同时有两个点被ccf的老爷机卡常。

大概就是说注意到每次操作只会修改某一行和最后一列,我们就对每一行和最后一列分别开一棵线段树。每次操作的时候就对第x行和最后一列的线段树各种操作一下。由于每一行和最后一列最多会被插入q个数,所以每棵线段树的大小要开大q。
这样显然会炸空间。但注意到一开始每行线段树的前m个位置是满的且元素是公差为1的等差数列,我们便可以先不开这部分的节点,等要用到的时候再把那些节点加上去。这样就可以保证空间了。
超级好打!
时间复杂度O(nlogn),空间复杂度O(nlogn)。

据说正解是树状数组但并不是太会。。。

代码

#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;

const int N=300005;

int n,m,q,sz,num[N],rt[N],tag,z;
struct tree{int s,l,r;LL id;}t[N*50];

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

LL kth(int &d,int l,int r,int k)
{
    if (!d) d=++sz,t[d].s=max(0,min(r,z)-l+1);
    t[d].s--;
    if (l==r)
    {
        if (l<=z) {tag=1;return l;}
        else return t[d].id;
    }
    int mid=(l+r)/2,ls=(!t[d].l?max(0,min(mid,z)-l+1):t[t[d].l].s);
    if (ls>=k) return kth(t[d].l,l,mid,k);
    else return kth(t[d].r,mid+1,r,k-ls);
}

void ins(int &d,int l,int r,int x,LL y)
{
    if (!d) d=++sz,t[d].s=max(0,min(r,z)-l+1);
    t[d].s++;
    if (l==r) {t[d].id=y;return;}
    int mid=(l+r)/2;
    if (x<=mid) ins(t[d].l,l,mid,x,y);
    else ins(t[d].r,mid+1,r,x,y);
}

int main()
{
    n=read();m=read();q=read();
    for (int i=1;i<=n;i++) num[i]=m-1,rt[i]=++sz,t[sz].s=m-1;
    num[0]=n;rt[0]=++sz;t[sz].s=n;
    for (int i=1;i<=q;i++)
    {
        int x=read(),y=read();
        if (y0;z=m-1;
            LL id1=kth(rt[x],1,m+q,y);
            if (tag) id1=(LL)(x-1)*m+id1;
            printf("%lld\n",id1);
            tag=0;z=n;
            LL id2=kth(rt[0],1,n+q,x);
            if (tag) id2=(LL)id2*m;
            num[0]++;z=n;ins(rt[0],1,n+q,num[0],id1);
            num[x]++;z=m-1;ins(rt[x],1,m+q,num[x],id2);
        }
        else
        {
            tag=0;z=n;
            LL id1=kth(rt[0],1,n+q,x);
            if (tag) id1=(LL)id1*m;
            printf("%lld\n",id1);
            num[0]++;z=n;ins(rt[0],1,n+q,num[0],id1);
        }
    }
    return 0;
}

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