hdu 4973 线段树

A simple simulation problem.

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 698    Accepted Submission(s): 274


Problem Description
There are n types of cells in the lab, numbered from 1 to n. These cells are put in a queue, the i-th cell belongs to type i. Each time I can use mitogen to double the cells in the interval [l, r]. For instance, the original queue is {1 2 3 3 4 5}, after using a mitogen in the interval [2, 5] the queue will be {1 2 2 3 3 3 3 4 4 5}. After some operations this queue could become very long, and I can’t figure out maximum count of cells of same type. Could you help me?
 

Input
The first line contains a single integer t (1 <= t <= 20), the number of test cases.

For each case, the first line contains 2 integers (1 <= n,m<= 50000) indicating the number of cell types and the number of operations.

For the following m lines, each line represents an operation. There are only two kinds of operations: Q and D. And the format is:

“Q l r”, query the maximum number of cells of same type in the interval [l, r];
“D l r”, double the cells in the interval [l, r];

(0 <= r – l <= 10^8, 1 <= l, r <= the number of all the cells)
 

Output
For each case, output the case number as shown. Then for each query "Q l r", print the maximum number of cells of same type in the interval [l, r].

Take the sample output for more details.
 

Sample Input
  
  
1 5 5 D 5 5 Q 5 6 D 2 3 D 1 2 Q 1 7
 

Sample Output
  
  
Case #1: 2 3
 

Source
2014 Multi-University Training Contest 10

/*有两种操作
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的纽带,根据左端点求出右端点。*/
#include<stdio.h>
#define N 50005
typedef long long ll;
struct node
{
   int x,y;
   ll sum,mx,p;
}a[N*4];
ll max(ll a,ll b)
{
  return a>b?a:b;
}
void pushup(int t)
{
   int temp=t<<1;
    a[t].sum=a[temp].sum+a[temp+1].sum;
    a[t].mx=max(a[temp].mx,a[temp+1].mx);
}
void build(int t,int x,int y)
{
    a[t].x=x; a[t].y=y; a[t].p=0;
    if(x==y)
    {
      a[t].sum=a[t].mx=1;
      return;
    }
    int mid=(x+y)>>1,temp=t<<1;
    build(temp,x,mid);
    build(temp+1,mid+1,y);
    pushup(t);
}
void pushdown(int t)
{
  int temp=t<<1;
  if(a[t].p)
  {
      a[temp].p+=a[t].p;
      a[temp+1].p+=a[t].p;
      a[temp].sum<<=a[t].p;
      a[temp+1].sum<<=a[t].p;
      a[temp].mx<<=a[t].p;
      a[temp+1].mx<<=a[t].p;
      a[t].p=0;
  }
}
void update(ll st,ll x,ll y,int t)
{//st是该节点的左端点在序列中的下标,那么可知这个节点所在区间是[st,st+a[t].sum-1]。
     if(st==x&&st+a[t].sum-1==y)
     {
         a[t].p++;
         a[t].sum*=2;
         a[t].mx*=2;
         return;
     }
     if(a[t].x==a[t].y)
     {//更新部分
       a[t].sum+=(y-x)+1;
       a[t].mx=a[t].sum;
       return;
     }
      pushdown(t);
     int temp=t<<1;
     ll mid=st+a[temp].sum-1;
     if(y<=mid)
         update(st,x,y,temp);
     else if(x>mid)
         update(mid+1,x,y,temp+1);
     else
     {
        update(st,x,mid,temp);
        update(mid+1,mid+1,y,temp+1);
     }
     pushup(t);
}
ll query(ll st,ll x,ll y,int t)
{
   ll ans;
    if(st==x&&y==st+a[t].sum-1)
        return a[t].mx;
    if(a[t].x==a[t].y)
        return (y-x+1);
    int temp=t<<1;
    pushdown(t);
    ll mid=st+a[temp].sum-1;
    if(y<=mid)
       ans=query(st,x,y,temp);
    else if(x>mid)
       ans=query(mid+1,x,y,temp+1);
    else
    {
        ll m1,m2;
        m1=query(st,x,mid,temp);
        m2=query(mid+1,mid+1,y,temp+1);
        ans=max(m1,m2);
    }
    pushup(t);
    return ans;
}
int main()
{
  int t,cnt=1,n,m;
  char str[10];
  ll l,r;
  //freopen("a.txt","r",stdin);
  scanf("%d",&t);
  while(t--)
  {
    scanf("%d%d",&n,&m);
    build(1,1,n);
    printf("Case #%d:\n",cnt++);
    while(m--)
    {
        scanf("%s%I64d%I64d",str,&l,&r);
        if(str[0]=='D')
          update(1,l,r,1);
        else
           printf("%I64d\n",query(1,l,r,1));
    }
  }
  return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值