第十四届华中科技大学程序设计竞赛M题:Fruits(线段树区间合并)


题目链接:点击打开链接


题目大意:

有n个数,m次操作

1.将区间内所有的数变为x

2.查询l r区间切割k的之后的value值(把一段区间切割k次之后,这个k+1个区间的value值之和最大是多少)

一个区间的value值就是这个区间内每个子间隔中不同连续数字的总和,总体来说就是 1 1 2 2 1 1 就算3


解题思路:

其实我们可以发现,区间切割在正常情况下每次切割都是将最后的答案+1,例如一个区间内所有数相等的情况。但是有一种情况切割对答案是没有影响的。

例如 1 1 1 2 2 2 ,这时我们会发现我们如果切割在1 2 之间 的这个位置 ,这次切割就对最终答案是没有影响的。所以我们要尽量选择切割两边数有相同的切割点,那么我们就需要统计这个区间有多少段连续的相同的数 例如 1 1 2 2 3 3 的值就是3 这样的。

之后统计哪些切割位置对答案有贡献最后记录结果就好了,维护过程就是线段树的区间维护,记录每个区间的最左值和最右值,合并的时候如果lson的最右值和rson的最左值相同的话,当前的sum就是lson.sum+rson.sum-1,否则就正常相加,



Ac代码:

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int INF=1e9+7;
const int mod=998244353;
int n,m,a[maxn];
struct node
{
    int l,r,sum,lazy;
    int lx,rx;
}t[maxn<<2];
void pushup(int rt) //pushup
{
    t[rt].sum=t[lson].sum+t[rson].sum; 
    if(t[lson].rx==t[rson].lx) t[rt].sum--;  //判断是否需要减1
    t[rt].lx=t[lson].lx,t[rt].rx=t[rson].rx;
}
void pushdown(int rt)
{
    if(t[rt].lazy)
    {
        t[lson].lx=t[lson].rx=t[rt].lx; //正常的pushdown操作
        t[rson].lx=t[rson].rx=t[rt].rx;
        t[lson].lazy=t[rson].lazy=1;
        t[lson].sum=t[rson].sum=1;
        t[rt].lazy=0;
    }
}
void build(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    t[rt].l=l,t[rt].r=r;
    if(l==r)
    {
        t[rt].lx=t[rt].rx=a[l];
        t[rt].sum=1;
        return ;
    }
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}
void update(int l,int r,int x,int rt)   //正常的区间更新
{
    int mid=(t[rt].l+t[rt].r)>>1;
    if(l<=t[rt].l&&t[rt].r<=r)
    {
        t[rt].lx=x,t[rt].rx=x;
        t[rt].lazy=1,t[rt].sum=1;
        return ;
    }
    pushdown(rt);
    if(l<=mid) update(l,r,x,lson);
    if(r>mid)  update(l,r,x,rson);
    pushup(rt);
}
int query(int l,int r,int rt)   //查询
{
    int mid=(t[rt].l+t[rt].r)>>1,ans=0;
    if(l<=t[rt].l&&t[rt].r<=r)
        return t[rt].sum;
    pushdown(rt);
    if(l<=mid)
    {
        ans+=query(l,r,lson);
        if(r>mid&&t[rson].lx==t[lson].rx) ans--;    //注意这个地方--
    }
    if(r>mid)
        ans+=query(l,r,rson);
    pushup(rt);
    return ans;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        build(1,n,1);
        while(m--)
        {
            int flag,l,r,x;
            scanf("%d%d%d%d",&flag,&l,&r,&x);
            if(flag==1) update(l,r,x,1);
            if(flag==2)
            {
                int ans=query(l,r,1);
                printf("%d\n",ans+min(r-l-ans+1,x));    //这个地方就是统计出有哪些间隔是有价值的 取它和k的较小值加上即可
            }
        }
    }
}


猜你喜欢

转载自blog.csdn.net/f2935552941/article/details/80232247