线段树专题

1.LCIS

题意:给你N个数,有两种操作,U a b, 将第a个数替换为b, Q a b查询第a个数到第b个数之间最长连续上升子序列

算法:定义线段树的节点为如下:

struct SEG

{

int l, r, v;
int M_len; //区间中间最多多长
int L_len,R_len,Lv,Rv; //区间最左多长,左边界值Lv, 区间最右多长,右边界值,Rv
int lazy; //延迟操作
int len; //该区间的最长上升长度
}seg[MAXN*4];

核心函数update

void update(int root)
{
int l1 = LL(root);
int l2 = RR(root);
int flag = 0;
seg[root].lazy = 0;
if( seg[l1].Rv < seg[l2].Lv ) //如果该区间中间连续
{
seg[root].M_len = seg[l1].R_len + seg[l2].L_len;
seg[root].M_len = max(seg[root].M_len, max(seg[l1].M_len,seg[l2].M_len));
flag = 1;
}
else
{
seg[root].M_len = max(seg[l1].M_len, max(seg[l2].M_len,seg[l1].R_len));
seg[root].M_len = max(seg[root].M_len, seg[l2].L_len);
}
if( flag && (seg[l1].L_len == (seg[l1].r - seg[l1].l + 1) ) ) //更新左边最长序列
{
seg[root].L_len = seg[l1].L_len + seg[l2].L_len;
}
else
seg[root].L_len = seg[l1].L_len;
if( flag && (seg[l2].R_len == (seg[l2].r - seg[l2].l + 1) ) ) //更新右边最长序列
{
seg[root].R_len = seg[l1].R_len + seg[l2].R_len;
}
else
seg[root].R_len = seg[l2].R_len;
seg[root].Rv = seg[l2].Rv;
seg[root].Lv = seg[l1].Lv;
seg[root].len = max(seg[root].M_len, max(seg[root].L_len,seg[root].R_len)); //保存最大序列
//test(root,-2);

}

View Code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXN 100010
#define LL(x) ( (x<<1) )
#define RR(x) ( (x<<1) + 1 )

int num[MAXN];

struct SEG
{
  int l, r, v;
  int M_len; //区间中间最多多长
  int L_len,R_len,Lv,Rv; //区间最左多长,左边界值Lv, 区间最右多长,右边界值,Rv
  int lazy; //延迟操作
  int len; //该区间的最长上升长度
}seg[MAXN*4];

const int inf = 0x7f7f7f7f;
void update(int);
void modify(int);

int max(int x, int y)
{
  return x > y ? x : y;
}

void build(int l, int r, int root)
{
   seg[root].l = l, seg[root].r = r;
   seg[root].lazy = 1;
   if ( l == r )  { seg[root].v = num[l]; modify(root); return; }
   int mid = (l+r)>>1;
   build(l,mid,root<<1);
   build(mid+1,r,(root<<1)+1);
   update(root);
}

void modify(int root)
{
   seg[root].len = seg[root].M_len = seg[root].R_len = seg[root].L_len = 1;
   seg[root].Lv = seg[root].Rv = seg[root].v; 
   seg[root].lazy = 0;
}

void replace(int pos, int v, int root)
{
   int l, r, mid;
   l = seg[root].l, r = seg[root].r, mid = (l+r) >> 1;
   seg[root].lazy = 1; //表示已线段下已有节点修改
   if( l == r ) { seg[root].v = v; modify(root); return; }
   if( mid >= pos )
    replace(pos, v, root << 1 );
   else
    replace(pos, v, (root << 1) + 1);
   update(root);
   
}

void test(int root, int flag)
{
  printf("flag: %d\n",flag);
  printf("l = %d, r = %d, L_len = %d, R_len = %d, M_len = %d\n", seg[root].l, seg[root].r, seg[root].L_len, seg[root].R_len, seg[root].M_len);
}

void update(int root)
{
    int l1 = LL(root);
    int l2 = RR(root);
    int flag = 0;
    seg[root].lazy = 0;
    if( seg[l1].Rv < seg[l2].Lv ) //如果该区间中间连续
    {
       seg[root].M_len = seg[l1].R_len + seg[l2].L_len;
       seg[root].M_len = max(seg[root].M_len, max(seg[l1].M_len,seg[l2].M_len)); 
       flag = 1;
    }
    else
    {
       seg[root].M_len = max(seg[l1].M_len, max(seg[l2].M_len,seg[l1].R_len));
       seg[root].M_len = max(seg[root].M_len, seg[l2].L_len);
    }
    if( flag && (seg[l1].L_len == (seg[l1].r - seg[l1].l + 1) ) )
    {
       seg[root].L_len = seg[l1].L_len + seg[l2].L_len;
    }
    else
       seg[root].L_len = seg[l1].L_len;
    if( flag && (seg[l2].R_len == (seg[l2].r - seg[l2].l + 1) ) )
    {
          seg[root].R_len = seg[l1].R_len + seg[l2].R_len;
    }
    else
          seg[root].R_len = seg[l2].R_len;
    seg[root].Rv = seg[l2].Rv;
    seg[root].Lv = seg[l1].Lv;
    seg[root].len = max(seg[root].M_len, max(seg[root].L_len,seg[root].R_len));
    //test(root,-2);

}

int query(int l, int r, int root)
{
  int ll, rr, mid;
  ll = seg[root].l, rr = seg[root].r, mid = (ll+rr) >> 1;
  //test(root,-1);
  if( l == seg[root].l && r == seg[root].r )
  {
     return seg[root].len;
  }
  if( mid >= r )
  {
       int ans = query(l,r,root << 1);
       //test(root,1);
       return ans;
  }
  else if( mid < l )
  {
       int ans = query(l, r, (root<<1) + 1);
       //test(root,2);
       return ans;
  }
  else
  {
       int aa = query(l,mid,(root<<1));
       int bb = query(mid + 1, r,(root<<1) + 1);
       //test(root,3);
       int l1 = LL(root); //左孩子
       int l2 = RR(root); //右孩子 
       int t1 = seg[l1].R_len <= (mid - l + 1) ? seg[l1].R_len : (mid - l + 1);
       int t2 = seg[l2].L_len <= (r - mid ) ? seg[l2].L_len : (r-mid);
       int tt = 0;
       if( seg[l1].Rv < seg[l2].Lv )
       {
           tt = t1 + t2; 
       }
       return max(tt,max(aa,bb));
       
          
  }
        

}

void print(int root)
{
 if( seg[root].l == seg[root].r ) { printf("%d ",seg[root].v); return; }
 print(root<<1);
 print((root<<1)+1);
}

int main( )
{
  int T,n,m,a,b,i;
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d%d",&n,&m);
    for(i = 1; i <= n; ++i)
    scanf("%d",&num[i]);
    memset(seg, 0, sizeof(seg));
    build(1,n,1);
    char str[10];
    //print(1);puts("");
    for(i = 1; i <= m; ++i)
    {
       scanf("%s%d%d",str,&a,&b);
       if( str[0] == 'U' )
       {
          replace(a+1,b,1);

       }
       else if( str[0] == 'Q' )
       {
          ++a,++b;
          printf("%d\n",query(a,b,1));
       }

    }
    //print(1);puts("");
       
  }
  return 0;
}

 2. 给区间置1,问最后没有被置1的点有多少个?

 算法:

 HASH,线段树,树状数组都可以过,不过姿势要优美,该剪枝的要剪枝

代码:

View Code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;

#define MAXN 20010
#define LL(x) ( (x<<1) )
#define RR(x) ( (x<<1) + 1 )

int num[MAXN];

struct SEG
{
  int l, r, v, len;
}seg[MAXN*4];


int max(int x, int y)
{
  return x > y ? x : y;
}

void build(int l, int r, int root)
{
   seg[root].l = l, seg[root].r = r;
   seg[root].len = 0;
   if ( l == r )  { seg[root].v = 0; return; }
   int mid = (l+r)>>1;
   build(l,mid,root<<1);
   build(mid+1,r,(root<<1)+1);
}

void Up(int root )
{
  seg[root].len = seg[root<<1].len + seg[(root<<1)+1].len;
}

void update(int l, int r, int root)
{
  int mid = (seg[root].l + seg[root].r) / 2;
  if( seg[root].len == seg[root].r - seg[root].l + 1 ) //必须加上这个剪枝,否则TLE
    return;
  if( l > r ) return;
  if( l <= seg[root].l && r >= seg[root].r )
  {
     seg[root].len = ( seg[root].r - seg[root].l + 1 );
     return;
  }
  if( mid >= r )
      update(l,r,root<<1);
  else if( mid < l )
      update(l,r,(root<<1) +1);
  else
  {
      update(l,mid,(root<<1));
      update(mid + 1, r,(root<<1) + 1);
  }
  Up(root);
}



int main( )
{
  int n,m,a,b,i;
  while( scanf("%d%d",&n,&m) != EOF )
  {
    //memset(seg, 0, sizeof(seg));
    build(1,n,1);
    for(i = 1; i <= m; ++i)
    {
       scanf("%d%d",&a,&b);
       if( a > b ) swap(a,b);
       update(a,b-1,1);
    }
    printf("%d\n",n-seg[1].len);
  }
  return 0;
}

 

posted on 2013-04-11 12:08  luckyboy1991  阅读(177)  评论(0编辑  收藏  举报