hdu-1540線段樹刷題
title: hdu-1540線段樹刷題
date: 2018-10-18 19:55:21
tags:
- acm
- 刷題
categories: ACM-線段樹
概述
哇,,,這道線段樹的題可以說是到目前為止我所做過的最難的一道了吧QAQ,,,,,,
一開始讀完題就是一臉懵逼,,,,完全不知道該從哪裡下手,,,就是知道這是一道線段樹的題也不知道該怎麼下手啊啊啊,,,,
最後還是看了kaungbin大佬的程式碼,,,QAQ
光是讀程式碼就花了一兩個小時,,,(不過也有可能和今天賊困有關,,,腦袋不怎麼轉啊
分析思路
題意
大概的題意就是一串在一條線上的村莊,,或者說是點,,,一開始都為1,,,然後有三種不同的操作,,,
- d a: 意味著將a這個點置為0,,,
- q a: 意味著詢問a周圍有多少的1,,,只要碰到零就不算了,,,例如110111110,,(q 5) = 5
- r: 意味著將上一個被置為零的點置為1
分析
我的想法
一開始我看到有需要上一次操作的情況,,就想著要將這些d操作儲存下來,,適合這道題的就是棧,,,
然後就是詢問了,,,我那時想著既然要求a周圍這些1的個數,,那我就找到兩端的0不就行了,,,然後從這裡就徹底的腦抽了,,,又想著用線段樹去求這段區間的和,,,,然後結果顯而易見,,,,t了,,,
因為,,這種想法線段樹根本沒有用啊!!!!都找出那兩端的0所在的位置直接減不就行了,,,這不就是裸暴力嗎,,,,,哇,,,被自己蠢哭(๐॔˃̶ᗜ˂̶๐॓),,,,
斌神的做法
首先將這段線劃分成多個區段,,,每個區段儲存的資訊有:從這去區段的左端點開始最長的連續1的個數ll 、 從這個區段右端點開始的最長的連續1的個數rl 、 還有這個區段最大的連續點的個數ml。。。
建樹:ll = rl = ml = 區間長
更新:
葉子節點置一置零,,,
左右遞迴更新
其他區間:(pushup())
父節點.ll = 左節點.ll 父節點.rl = 右節點.rl
父節點.ml取左右節點的最大的一個ml
若左節點的rl + 右節點的ll > 父節點的ml,,,,就更新為前者
對於父節點的ll,rl
如果左節點的ll為左節點的長度,,,就說明左節點從左端點開始的連續1的最大的個數就為左節點包含的點的個數,,,所以此時的父節點的ll就要和右節點的ll合併
同理,,,父節點的rl也要進行這樣的判斷
查詢:
對於一些特殊的區間直接返回該區間的最大的連續1的個數也就是ml
當loc在中點左時,,,就要從左節點來判斷,,,判斷的條件是loc是否超出了rl的最左端(畫圖更容易理解一些),,,超出的話就說明loc所在的連續的1一部分是在左節點的rl裡另一部分是在右節點的ll裡,,,就分成兩個點查詢,,,一個是在左節點的loc,,,另一個時在右節點的mid+1那個點
同理,,若在中點的右時也有類似的判斷,,,
大體上說就是不斷地判斷要找到那個點相對ll,rl的位置,,,最後把遞迴查詢到的結果合併就行了,,,
字醜見諒,,,,(不過應該沒人看把,,,,
程式碼
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int maxn = 5e5 + 10;
struct node
{
int l;
int r;
int ml;
int ll;
int rl;
}node[maxn << 2];
void build(int rt , int l , int r)
{
node[rt].l = l;
node[rt].r = r;
node[rt].ml = node[rt].ll = node[rt].rl = r - l + 1; //剛開始肯定是區間的長度
if(l == r) return;
int mid = (l + r) >> 1;
build(rt << 1 , l , mid);
build(rt << 1 | 1 , mid + 1 , r);
return;
}
void update(int rt , int loc , int val)
{
if(node[rt].l == node[rt].r)
{
if(val) node[rt].ml = node[rt].ll = node[rt].rl = 1; //摧毀和重建兩種
else node[rt].ml = node[rt].ll = node[rt].rl = 0;
return;
}
int mid = (node[rt].l + node[rt].r) >> 1;
if(loc <= mid) update(rt << 1 , loc , val);
else update(rt << 1 | 1 , loc , val);
//遞迴更新
//先更新父節點的兩個,ll,rl
node[rt].ll = node[rt << 1].ll;
node[rt].rl = node[rt << 1 | 1].rl;
//然後是父節點的ml
node[rt].ml = max(node[rt << 1].ml , node[rt << 1 | 1].ml);
node[rt].ml = max(node[rt].ml , node[rt << 1].rl + node[rt << 1 | 1].ll);
//父節點的ll,rl可能就是左右節點的ll,,rl,,,,當剛好是子節點的全部時還要加上另一個區間的一部分
if(node[rt << 1].ll == node[rt << 1].r - node[rt << 1].l + 1)
node[rt].ll += node[rt << 1 | 1].ll;
if(node[rt << 1 | 1].rl == node[rt << 1 | 1].r - node[rt << 1 | 1].l + 1)
node[rt].rl += node[rt << 1].rl;
return;
}
int query(int rt , int loc)
{
//特殊情況直接返回ml
if(node[rt].l == node[rt].r || node[rt].ml == 0 || node[rt].ml == node[rt].r - node[rt].l + 1)
return node[rt].ml;
int mid = (node[rt].l + node[rt].r) >> 1;
if(loc <= mid)
{
if(loc >= node[rt << 1].r - node[rt << 1].rl + 1)
return query(rt << 1 , loc) + query(rt << 1 | 1 , mid + 1);
else
return query(rt << 1 , loc);
}
else
{
if(loc <= node[rt << 1 | 1].l + node[rt << 1 | 1].ll - 1)
return query(rt << 1 | 1 , loc) + query(rt << 1 , mid);
return query(rt << 1 | 1 , loc);
}
}
int main()
{
int n , m;
while(scanf("%d%d" , &n , &m) != EOF)
{
build(1 , 1 , n);
int q[maxn];
int toc = 0;
int t = 0;
while(m--)
{
char c;scanf(" %c" , &c);
if(c == 'D')
{
scanf("%d" , &t);
q[toc++] = t; //把摧毀操作儲存
update(1 , t , 0);
}
else if(c == 'Q')
{
scanf("%d" , &t);
printf("%d\n" , query(1 , t));
}
else
{
if(t)
{
t = q[--toc];
update(1 , t , 1);
}
}
}
}
return 0;
}
//kaungbin