线段树专题
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; }

浙公网安备 33010602011771号