HDU 5372 Segment Game (树状数组+离散化)*

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5372

#include<bits/stdc++.h>
#pragma comment(linker,"/STACK:1024000000,1024000000")
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define read(x,y) scanf("%d%d",&x,&y)

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ll long long
const int  maxn =2e5+5;
const int mod=1e9+7;
ll powmod(ll x,ll y) {ll t;for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod;return t;}
ll gcd(ll x,ll y)  {  return y==0?x:gcd(y,x%y); }
///树状数组的结构
int l[maxn],r[maxn];///统计左端点和右端点的数量。
/*
题目大意:n次操作,每次操作有两种,
一种是添加线段,线段的长度就是添加操作的序号,额外输入一个左端点,
一种是删除第几次添加操作的线段。

这道题比较绕人,特别考验逻辑,
首先观察到残忍的数据范围开到九次方,离散化处理没跑了。
先分析下,如何统计完整的区间段,我们模拟一下,
如果查询a到b范围内的线段数量,
分类下,如果一个线段c-d,关系是这样:c-d-a-b,
那么明显不计数,c-a-d-b也不计数,a-c-d-b计数,
a-c-b-d不计数,a-b-c-d不计数。

只要统计b区间的右端点,和a区间的左端点数量就行了,
不难发现所有的情况这种算法都符合要求。

下面就是离散化的过程,把询问都存储起来,
把每个左端点和右端点,都离散映射成大小序号关系。
至于删除操作的存在,我们要额外存储添加操作的集合,
单射就可以 ,因为序号就是区间的长度。
*/

int lowbit(int x)
{
    return x&-x;
}
int sum(int x,int mark)
{
    int ret=0;
    for(;x>0;x-=lowbit(x))
    {
        if(mark) ret+=r[x];
        else ret+=l[x];
    }
    return ret;
}
void add(int x,int d,int mark)
{
    for(;x<maxn;x+=lowbit(x))
    {
        if(mark) r[x]+=d;
        else l[x]+=d;
    }
}
int op[maxn],s[maxn];///询问集合和区间端点集合。
int L[maxn],cntL,R[maxn],cntR;///存储左右的边
int q[maxn],Q;///添加的边的集合

int n;
void init()
{
    Q=cntR=cntL=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&op[i],&s[i]);
        if(op[i]==0)
        {
            q[++Q]=s[i];///s[i]是左端点或者编号
            R[cntR++] = s[i]+Q;
            L[cntL++]  = s[i];
        }
    }
    sort(L,L+cntL);cntL=unique(L,L+cntL)-L;
    sort(R,R+cntR);cntR=unique(R,R+cntR)-R;
    memset(l,0,sizeof(l));
    memset(r,0,sizeof(r));///初始化树状数组
}

void solve()
{
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(op[i])
        {
            int v=q[s[i]];
            int p=lower_bound(L,L+cntL,v)-L+1;
            int q=lower_bound(R,R+cntR,v+s[i])-R+1;
            add(p,-1,0);
            add(q,-1,1);
        }
        else
        {
            int lb=q[++cnt],rb=lb+cnt;
            int p=lower_bound(L,L+cntL,lb)-L+1;
            int q=lower_bound(R,R+cntR,rb)-R+1;
            int ans=sum(q,1)-sum(p-1,0);///答案存储
            add(p,1,0);add(q,1,1);
            printf("%d\n",ans);
        }
    }
}

int main()
{
    int ca=1;
    while(scanf("%d",&n)!=EOF)
    {
        init();///初始化
        printf("Case #%d:\n",ca++);
        solve();
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值