Luogu P3616 富金森林公园

树状数组+离散化+转化

  一道树状数组好题!我们先来一发弱化版:如果只查询有多少个石头,怎么做?很简单,我们需要考虑权值树状数组,先离散化(因为我们只需要考虑高矮关系),如何离散化?考虑离线,将可能改变的值都存下来,离散化一下。每次的查询通过二分来找到对应的离散化之后的值。之后就是权值树状数组的常规操作了,将之前的贡献减去,将新来的贡献加上。但是,这道题问的是有多少个连续的部分。考虑去重,如果一个点比她左边的一个点海拔要低,那么就会重复计数,那么就应该在她所对应的权值上减去1,同理,如果她比她右边的点海拔低,那么也要减1.最后查询一个数x,就是查询去掉[1,x-1]的影响后的值,可以用前缀和实现。
注意特判边界:1、n!!!!!!!!!!!!

code:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
#define int long long 
using namespace std;
const int maxn=2000006;
#define half (l+r)>>1
struct hzw
{
    int rhi,hi;
    int id;
}s[maxn];
struct zmd
{
    int id;
    int x,y;
}q[maxn];
inline bool cmp1(hzw a,hzw b)
{
    return a.rhi<b.rhi;
}
inline bool cmp2(hzw a,hzw b)
{
    return a.id<b.id;
}
int n,m,tot,val[maxn];
inline int search(int k)
{
    int l=1,r=tot+1,ans=1e9;
    while (l<=r)
    {
        int mid=half;
        if (s[mid].rhi>=k)
        {
            r=mid-1;
            ans=min(ans,s[mid].hi);
        }
        else l=mid+1;
    }
    if (ans==1e9) return tot+1;
    return ans;
} 
inline void update(int x,int k)
{
    if (!x) return;
    if (x>tot) return;
    for (int i=x;i<=tot;i+=(i&(-i)))
    {
        val[i]+=k;
    }
}
inline int query(int x)
{
    int ans=0;
    if (!x) return 0;
    if (x>tot) x=tot;
    for (int i=x;i>0;i-=i&(-i))
    {
        ans+=val[i];
    }
    return ans;
}
signed main()
{
    cin>>n>>m;
    for (int i=1;i<=n;++i)
    {
        scanf("%lld",&s[i].rhi);
        s[i].id=i;
    }
    tot=n;
    for (int i=1;i<=m;++i)
    {
        scanf("%lld",&q[i].id);
        if (q[i].id==1) scanf("%lld",&q[i].x);
        else 
        {
            scanf("%lld%lld",&q[i].x,&q[i].y);
            s[++tot].id=tot,
            s[tot].rhi=q[i].y;
        }
    }
    sort(s+1,s+1+tot,cmp1);
    for (int i=1;i<=tot;++i)
    {
        if (s[i].rhi==s[i-1].rhi) s[i].hi=s[i-1].hi;
        else s[i].hi=s[i-1].hi+1;
    }
    for (int i=1;i<=m;++i)
    {
        if (q[i].id==2) continue;
        q[i].x=search(q[i].x);
    }
    sort(s+1,s+1+tot,cmp2);
    int cnt=n;
    for (int i=1;i<=n;++i) 
    {
        update(s[i].hi,1);
        update(min(s[i].hi,s[i-1].hi),-1);
    }
    for (int i=1;i<=m;++i)
    {
        if (q[i].id==1)
        {
            printf("%lld\n",query(tot)-query(q[i].x-1));
            continue;
        }
        else 
        {
            cnt++;
            update(s[q[i].x].hi,-1);
            if (q[i].x!=1)update(min(s[q[i].x].hi,s[q[i].x-1].hi),1);
            if (q[i].x!=n)update(min(s[q[i].x].hi,s[q[i].x+1].hi),1);
            s[q[i].x].hi=s[cnt].hi;
            update(s[q[i].x].hi,1);
        if (q[i].x!=1)update(min(s[q[i].x].hi,s[q[i].x-1].hi),-1);
        if (q[i].x!=n)update(min(s[q[i].x].hi,s[q[i].x+1].hi),-1);
        }
    }
    return 0;  
}

总结:

1、类似这样有关于值的大小比较类问题要考虑用权值版,利用巧妙地离散化解决问题。
2、这种会重复计算的要考虑怎么去重;
3、注意边界

猜你喜欢

转载自www.cnblogs.com/bullshit/p/9712297.html