时间机器(CDQ分治)

 



      题解:第一眼瞄过去以为是个可持久化线段树(看来我还真菜),这题其实解法有很多,我就说说在考场上想到的CDQ分治。

                 首先这道题要按操作一步一步去做,而且操作还有时间的这个限制,一个操作i对另一操作j有贡献当且仅当该操作i在操作j前,且操作i的时间在操作j前,这是一个经典的二维偏序。我们把操作分成l,mid和mid+1,r两部分(这个按输入顺序分),先各自处理(l,mid)和(mid+1,r),然后处理l,mid对mid+1,r的贡献。

                 如何处理l,mid对mid+1,r的贡献呢,我们在此之前已经各自处理(l,mid)和(mid+1,r),即已把l,mid和mid+1,r按时间排好了,那么就进行一次类归并排序(按时间),用桶维护每个数出现的个数,按时间插入,that's ok。

                 然而记得桶要清空,而且不能从头清到尾,要插入了几个数就反向清空,不然时间会退化成n^2。

贴代码:

#include<algorithm>
#include<fstream>
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;

int n,tot,ans[100050],cnt[100050],me[100050];
struct tedge
{
    int id,t,x,num,ls;
}d[100050],op[100050];

void CDQ(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)/2;
    CDQ(l,mid);   
    CDQ(mid+1,r);
    
    int p1=l,p2=mid+1,p3=l;
    for (int i=1; i<=me[0]; i++)
    cnt[me[i]] = 0;
    me[0] = 0;
    while (p3<=r)
    {
        if (p1>mid||(p2<=r&&op[p2].t<op[p1].t))
        {
            if (op[p2].id==3) ans[op[p2].num]+=cnt[op[p2].ls];
            d[p3] = op[p2];
            p2++;
        }
        else
        {
            if (op[p1].id==1) {cnt[op[p1].ls]++; me[0]++; me[me[0]]=op[p1].ls;}
            if (op[p1].id==2) {cnt[op[p1].ls]--; me[0]++; me[me[0]]=op[p1].ls;}
            d[p3] = op[p1]; 
            p1++;
        }
        p3++;
    }
    for (int i=l; i<=r; i++)
    op[i] = d[i];
}

bool cmp1(tedge a,tedge b)
{
    return a.x<b.x;
}

bool cmp2(tedge a,tedge b)
{
    return a.num<b.num;
}

int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%d",&n);
    for (int i=1; i<=n; i++)
    {
        scanf("%d%d%d",&op[i].id,&op[i].t,&op[i].x);
        op[i].num = i;
    }
    sort(op+1,op+1+n,cmp1);
    for (int i=1; i<=n; i++)
    {
        if (op[i].x==op[i-1].x) op[i].ls = op[i-1].ls;
        else 
        {
            tot++;
            op[i].ls = tot;
        }
    }
    sort(op+1,op+1+n,cmp2);
    CDQ(1,n);
    sort(op+1,op+1+n,cmp2);
    for (int i=1; i<=n; i++)
    if (op[i].id==3) printf("%d\n",ans[op[i].num]);
    return 0;
}

 

 
posted @ 2017-09-26 14:11  最终惊吓者——Janous  阅读(155)  评论(0编辑  收藏  举报