HDU 5193 Go to movies Ⅱ 块状链表套树状数组

传送门:点击打开链接

Go to movies Ⅱ

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 84    Accepted Submission(s): 37


Problem Description
LeLe is tired of playing blocks , so he decides to go to the movies with his friends again.

When the conductor sees LeLe again , he thinks LeLe is a smart boy.If LeLe can accomplish the task given by her ,LeLe and his friends can enjoy a free film!

The task is :
All kids line up( n kids in total) and LeLe should find out how many pairs of kids stand in wrong position (the tall kids stands in front of the short kids. i<j && Hi>Hj ).As time goes by,some friends will join the line and some kids is so impatience that leave the line.LeLe should work out how many pairs of kids stand in wrong position when a kid leaves or joins.

However LeLe knows the relative height of all kids.The shortest is 1,and the tallest is n .
 


Input
There are multiple test cases, about 10 cases.

The first line of input contains two integers n,m(1n,m20000) .

The second line contains n integers H1,H2,...,Hn(1Hin) ,indicate the height of each kids in the initial queue from left to right.

For the next m lines ,each line means a kid leave or join.
0 x y means a kid of y height stands behind the xth kid, x=0 means stand at the front of the queue. (1yn)
1 x indicate the xth (from left to right) kid leave.
 


Output
There are multiple test cases, about 10 cases.
For each operation puts how many pairs of kids stand in wrong position.
 


Sample Input
  
  
5 5 5 4 3 2 1 0 0 2 0 1 3 1 3 1 3 1 3
 


Sample Output
  
  
11 13 9 6 4
Hint
After operator 1,the height of every kid is 2 5 4 3 2 1. The total pairs of wrong position is 11. After operator 2,the height of every kid is 2 3 5 4 3 2 1. The total pairs of wrong position is 13. After operator 3,the height of every kid is 2 3 4 3 2 1. The total pairs of wrong position is 9. After operator 4,the height of every kid is 2 3 3 2 1. The total pairs of wrong position is 6. After operator 5,the height of every kid is 2 3 2 1. The total pairs of wrong position is 4. All operators are legal.
 


Source

题意:求序列的动态逆序对数,0代表在某位置之后插入一个数,1代表删除某位置的数。每次操作完成之后输出当前序列的逆序数。
思路:数据量不大,只有2W。由于有插入的删除操作,考虑使用块状链表。每个块中建立一个安值建树树状数组,方便统计大于某数或小于某数的个数。当插入的时候,在插入的块之前的块,直接用树状数组求出比这个数大的数的个数,在插入的块之后的块,直接用数组数组求出比这个数小的数的个数,在当前块中,暴力求出比这个数小或大的数的个数。一次操作的时间复杂度为sqrt(n)+sqrt(n)。删除和插入类似。
代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int m=150;
inline int lowbit(int x)
{
    return x&-x;
}
void add(int c[],int pos,int x)
{
    while(pos<=20000)
    {
        c[pos]+=x;
        pos+=lowbit(pos);
    }
}
int sum(int c[],int l,int r)
{
    int sum1=0,sum2=0;
    l--;
    while(l>0)
    {
        sum1+=c[l];
        l-=lowbit(l);
    }
    while(r>0)
    {
        sum2+=c[r];
        r-=lowbit(r);
    }
    return sum2-sum1;
}
struct data
{
    int s,a[2*m+5];
    data *next;
    int c[20005];
    data()
    {
        memset(c,0,sizeof(c));
        next=NULL;
    }
};
data *root;
void insert(int x,int pos)
{
    if(root==NULL)
    {
        root=new data;
        root->s=1;
        root->a[1]=x;
        add(root->c,x,1);
        return ;
    }
    data *k=root;
    while(pos>k->s && k->next!=NULL)
    {
        pos-=k->s;
        k=k->next;
    }
    memmove(k->a+pos+1,k->a+pos,sizeof(int)*(k->s-pos+1));
    k->s++;
    k->a[pos]=x;
    add(k->c,x,1);
    if(k->s==2*m)
    {
        data *t=new data;
        t->next=k->next;
        k->next=t;
        memcpy(t->a+1,k->a+m+1,sizeof(int)*m);
        for(int i=1;i<=m;i++)
        {
            add(k->c,t->a[i],-1);
            add(t->c,t->a[i],1);
        }
        t->s=k->s=m;
    }
}
void del(int pos)
{
    data *k=root;
    while(pos>k->s && k->next!=NULL)
    {
        pos-=k->s;
        k=k->next;
    }
    add(k->c,k->a[pos],-1);
    memmove(k->a+pos,k->a+pos+1,sizeof(int)*(k->s-pos));
    k->s--;
}
int find(int pos)
{
    data *k=root;
    while(pos>k->s && k->next!=NULL)
    {
        pos-=k->s;
        k=k->next;
    }
    return k->a[pos];
}
int work(int pos)
{
    int res=0;
    data *k=root;
    int x=find(pos);
    while(pos>k->s && k->next!=NULL)
    {
        pos-=k->s;
        res+=sum(k->c,x+1,20000);
        k=k->next;
    }
    for(int i=1;i<pos;i++) if(k->a[i]>x) res++;
    for(int i=pos+1;i<=k->s;i++) if(k->a[i]<x) res++;
    while(k->next!=NULL)
    {
        k=k->next;
        res+=sum(k->c,1,x-1);
    }
    return res;
}
void destroy(data *k)
{
    if(k->next!=NULL) destroy(k->next);
    delete k;
}
int main()
{
    int n,p;
    while(~scanf("%d %d",&n,&p))
    {
        root=NULL;
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            insert(x,i);
            ans+=work(i);
        }
        while(p--)
        {
            int c;
            scanf("%d",&c);
            if(c==0)
            {
                int x,y;
                scanf("%d %d",&x,&y);
                x++;
                insert(y,x);
                ans+=work(x);
            }
            else
            {
                int x;
                scanf("%d",&x);
               // x++;
                ans-=work(x);
                del(x);
            }
            printf("%d\n",ans);
        }
        destroy(root);
    }
    return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值