kaiser終於成為冒險協會的一員,這次冒險協會派他去冒險,他來到一處古墓,卻被大門上的守護神擋住了去路,守護神給出了乙個問題,
只有答對了問題才能進入,守護神給出了乙個自然數序列a,每次有一下三種操作。
1,給出l,r,x,將序列l,r之間的所有數都 and x
2,給出l,r,x,將序列l,r之間的所有數都 or x
3,給出l,r,詢問l,r之間的最大值
第一行包含兩個整數 n,m 接下來一行包含 n 個整數, 表示a序列,接下來 m 行, 每行描述了乙個操作.
2<=n<=2e5 2<=q<=2e5,0<=ai<=2^20.
注意到普通線段樹的lazy標記不管用了,因為一段區間中同時and或or上乙個數字後最大值並不還是原來的最大值
我們考慮仍然用線段樹,將一段連續的、and上乙個數字後最大值不改變的區間成為同類區間,顯然我們需要把tag打在這些同類區間上才是科學的,那麼記錄一下每個區間的and和,or和,以及兩個操作的tag即可。注意tag下傳時and和or的優先順序,具體可以模擬乘法和加法同時存在的tag
複雜度需要用到勢能分析,實際操作可以檢驗這是不會t的(滑稽
#include
#include
#include
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
const
int n=200005;
int max[n<<2],sum[n<<2][2],tag[n<<2][2];
int rev[1148576],a[n];
int read()
void mark1(int now,int x)
void mark2(int now,int x)
void push_down(int now)
if (tag[now][1])
}void push_up(int now)
void modify1(int now,int tl,int tr,int l,int r,int v)
push_down(now);
int mid=(tl+tr)>>1;
if (l<=mid) modify1(now<<1,tl,mid,l,r,v);
if (r>mid) modify1(now<<1|1,mid+1,tr,l,r,v);
push_up(now);
}void modify2(int now,int tl,int tr,int l,int r,int v)
push_down(now);
int mid=(tl+tr)>>1;
if (l<=mid) modify2(now<<1,tl,mid,l,r,v);
if (r>mid) modify2(now<<1|1,mid+1,tr,l,r,v);
push_up(now);
}int query(int now,int tl,int tr,int l,int r)
void build_tree(int now,int tl,int tr)
int mid=(tl+tr)>>1;
build_tree(now<<1,tl,mid);
build_tree(now<<1|1,mid+1,tr);
push_up(now);
}int main(void) else
if (opt==2) else
printf("%d\n", query(1,1,n,l,r));
}return
0;}
bzoj5312 冒險 勢能均攤線段樹
bzoj5312 冒險 如果一次操作對區間 和 區間 產生的影響是相同的,那麼該操作對整個區間的影響都是相同的 對於每次操作,在某些位上的值,對於整個區間影響是相同的,對相同影響的操作直接打標記 否則遞迴子樹 複雜度證明 include includeinline int read while c ...
BZOJ5312 冒險 勢能均攤線段樹
題目鏈結 這玩意兒是聽shadowice說的,好像很厲害的樣子 我們維護出區間 區間 區間最大值 結論 如果一次操作對區間 和 區間 產生的影響是相同的,那麼該操作對整個區間的影響都是相同的 證明可以看這裡 然後就做完了。時間複雜度 o nklogn k 是二進位制位數,這裡是20 include ...
BZOJ5312 冒險 線段樹 位運算
kaiser終於成為冒險協會的一員,這次冒險協會派他去冒險,他來到一處古墓,卻被大門上的守護神擋住了去路,守護神給出了乙個問題,只有答對了問題才能進入,守護神給出了乙個自然數序列a,每次有一下三種操作。1,給出l,r,x,將序列l,r之間的所有數都 and x 2,給出l,r,x,將序列l,r之間的...