hdu 4973 A simple simulation problem.(线段树)

http://acm.hdu.edu.cn/showproblem.php?pid=4973


有两种操作

D l r 将【l,r】区间翻倍

Q l r询问[l,r]中相同数字出现的最多次数


比赛的时候脑子太乱了,没有想到怎么做。发现每次翻倍序列的长度都在变化,区间对应的数也在变,没有思路。

但是静下心来想一想,思路还是挺清晰的。

无论怎么翻倍,序列中的数都是连续的,范围是1~n。可以拿一个数组来记录每个数出现的次数,当更新或询问区间[l,r]时,可以利用其前缀和找到区间[l,r]对应的数字分别是lx,rx,对于lx+1,rx-1内的数字是完全翻倍的,可以用线段树维护区间的和相同数字的最大数目,由于l,r并不一定完全包含在lx.rx内,端点需要特殊处理。

那么重点就是怎样找到l,r对应的数更新区间[lx,rx],可以把每个数字所在区间的左端点作为连接l,r和lx,rx的纽带。这里想了许久才绕过来。

WA了几次,分别是:因为多次翻倍,输入的区间端点可能超int,要用__int64;push_dow的时候lazy标记要累加左右儿子,而不是直接赋值(经常犯nc的错误);query的时候忘记push_down(这个貌似更nc)


#include <stdio.h>
#include <iostream>
#include <map>
#include <set>
#include <list>
#include <stack>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#include <stdlib.h>
#include <algorithm>
#define LL __int64
#define eps 1e-12
#define PI acos(-1.0)
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 50010;

struct node
{
    int l,r;
    int lazy; //记录区间翻倍次数
    LL sum;//区间的和
    LL mx;//区间内相同数字出现的最多次数
}tree[maxn*4];

void push_up(int v)
{
    tree[v].mx = max(tree[v*2].mx,tree[v*2+1].mx);
    tree[v].sum = tree[v*2].sum + tree[v*2+1].sum;
    return;
}

void push_down(int v)
{
    if(tree[v].l == tree[v].r || tree[v].lazy == 0)
        return;
    tree[v*2].lazy += tree[v].lazy;//累加,累加
    tree[v*2+1].lazy += tree[v].lazy;
    tree[v*2].mx <<= (LL)tree[v].lazy;
    tree[v*2].sum <<= (LL)tree[v].lazy;
    tree[v*2+1].mx <<= (LL)tree[v].lazy;
    tree[v*2+1].sum <<= (LL)tree[v].lazy;
    tree[v].lazy = 0;
}

void build(int v, int l, int r)
{
    tree[v].l = l;
    tree[v].r = r;
    tree[v].lazy = 0;
    if(l == r)
    {
        tree[v].sum = (LL)1;
        tree[v].mx = (LL)1;
        return;
    }
    int mid = (l+r)>>1;
    build(v*2,l,mid);
    build(v*2+1,mid+1,r);
    push_up(v);
}

void update(int v, LL st, LL l, LL r)//st是该节点的左端点在序列中的下标,那么可知这个节点所在区间是[st,st+tree[v].sum-1]。
{
    if(st == l && st+tree[v].sum-1 == r)
    {
        tree[v].lazy++;
        tree[v].mx <<= (LL)1;
        tree[v].sum <<= (LL)1;
        return;
    }
    if(tree[v].l == tree[v].r) //针对左右端点lx,rx,它们不全在区间[l,r]内,只更新其部分
    {
        tree[v].sum += (LL)(r-l+1);
        tree[v].mx = tree[v].sum;
        return;
    }
    push_down(v);
    LL m = st + tree[v*2].sum - 1;
    if(r <= m)
        update(v*2,st,l,r);
    else if(l > m)
    {
        update(v*2+1,m+1,l,r);
    }
    else
    {
        update(v*2,st,l,m);
        update(v*2+1,m+1,m+1,r);
    }
    push_up(v);
}

LL query(int v, LL st, LL l, LL r)
{
    if(st == l && st+tree[v].sum-1 == r)
        return tree[v].mx;

    if(tree[v].l == tree[v].r)
        return r-l+1;
    push_down(v);
    LL m = st+tree[v*2].sum-1;

    if(r <= m)
        return query(v*2,st,l,r);
    else if(l > m)
        return query(v*2+1,m+1,l,r);
    else return max(query(v*2,st,l,m),query(v*2+1,m+1,m+1,r));
}

int main()
{
    int test;
    int n,m;
    LL l,r;
    char ch[4];
    scanf("%d",&test);
    for(int item = 1; item <= test; item++)
    {
        scanf("%d %d",&n,&m);
        build(1,1,n);
        printf("Case #%d:\n",item);
        while(m--)
        {
            scanf("%s %I64d %I64d",ch,&l,&r);
            if(ch[0] == 'D')
                update(1,1,l,r)
            else
                printf("%I64d\n",query(1,1,l,r));
        }
    }
    return 0;
}


你可能感兴趣的:(线段树)