HDU1540--線段樹(最長連續區間)
阿新 • • 發佈:2019-02-20
這個題目好像不是很難,可是我想了很久,還超時了。。我用的是普通線段樹和二分,第一次用二分超時啊啊啊啊啊
好,讓我們言歸正傳。(參考別的大佬的做法,賊6)
看到這個題目之後,能夠想到它是為了求包含一個點的最大連續區間。那麼多的區間,應該是要用線段樹來做。
可以想得到,查詢的時候,是從這個點展開,判斷左右是否連續。
首先,我們用遞迴查詢到這個點,或者說是包含這個點的連續區間,假設它是一個左子樹,那我們就要加上它相鄰的右子樹的從左邊開始的連續區間,如果它是右子樹,那麼就按相反。
這樣的話,我們不如就建立兩個陣列,一個是從左邊開始累加區間長度的陣列(lsum[]),一個是從右邊開始累加區間長度的陣列(rsum[]) ,然後建樹,更新節點,查詢。
還要提一點,由於題目中有一個什麼重建,emmm,就是先進後出的棧嘛,所以我們這裡就可以用到c++中的一個stack容器。
有這幾個常用的操作吧:
stack<int> s;
1.入棧:如s.push(x);
2.出棧:如 s.pop().注意:出棧操作只是刪除棧頂的元素,並不返回該元素。
3.訪問棧頂:如s.top();
4.判斷棧空:如s.empty().當棧空時返回true。
5.訪問棧中的元素個數,如s.size();
貼程式碼:
#include"cstdio" #include"stack" using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define maxn 50010 int lsum[4*maxn],rsum[4*maxn],flag; void Build(int l,int r,int rt) { lsum[rt] = rsum[rt] = r-l+1; if (l == r) return; int m = (l+r) >> 1; Build(lson); Build(rson); } void Pushup(int rt,int m) { lsum[rt] = lsum[rt<<1]; rsum[rt] = rsum[rt<<1|1]; if (lsum[rt] == m - (m>>1)) lsum[rt] += lsum[rt<<1|1];//yi cuo if (rsum[rt] == m>>1) rsum[rt] += rsum[rt<<1]; } void Updata(int p,int x,int l,int r,int rt) { if (l == r) { lsum[rt] = rsum[rt] = x; return; } int m = (l+r) >> 1; if (p <= m) Updata(p,x,lson); else Updata(p,x,rson); Pushup(rt,r-l+1); } int Query(int p,int l,int r,int rt) { if (rsum[rt] >= r-p+1) { flag = 1; return rsum[rt]; } if (lsum[rt] >= p-l+1) { flag = 1; return lsum[rt]; } if (l == r) return 0; int m = (l+r) >> 1, t = 0; if (p > m) { t = Query(p,rson); if (flag) { flag = 0; t += rsum[rt<<1]; } } else { t = Query(p,lson); if (flag) { flag = 0; t += lsum[rt<<1|1]; } } return t; } int main() { int n,m; // freopen("1.in","r",stdin); while(~scanf("%d%d",&n,&m)) { Build(1,n,1); char cmd[2]; stack<int> sta; for (int i = 0;i < m;i ++) { scanf("%s",cmd); if (cmd[0] == 'D') { int x; scanf("%d",&x); sta.push(x); Updata(sta.top(),0,1,n,1); } else if (cmd[0] == 'Q') { int x; flag = 0; scanf("%d",&x); printf("%d\n",Query(x,1,n,1)); } else { Updata(sta.top(),1,1,n,1); sta.pop(); } } } return 0; }