线段树维护区间信息的题单
dalao的博客
最长区间
带修改的严格最长上升子序列长度。【练习线段树做法】
要深入理解线段树的l, r是指的数组下标这句话emm.
一个节点保存:左边的LIS长度, 右边的LIS长度, 区间总体的LIS长度, 区间的元素个数(其实可以算出来)。
关键在于pushup函数:
先更新区间的llis,rlis,tlis.
然后如果左边最右的值<右边最左的值(这个可以用线段树中节点在数组的下标实现,因为l,r指的是下标):那左右两个区间就可以构成lis,所以可以继续更新:
如果左边的llis==左边的size,代表左边整个是一个最长上升子序列,那llis就可以更新;同理rlis也可以。
最后更新tlis可以是左边的rlis和右边的llis。
tips:可以与LCIS这个题比较一下, 其实这样直接query对于一般区间是不对的,不能简单左右区间答案相加,(例如1 2 3 4 1 2, 查询1到5,并不是4+1)。但是这个题一定是询问1到n的区间,所以可以直接加。
#include
#define ll long long
using namespace std;
const int N = 100010;
int n,m,w[N];
struct node{
int l, r, llis,rlis,tlis, sz;
}tr[N << 2];
void pushup(int u){
tr[u].sz = tr[u << 1].sz + tr[u << 1 | 1].sz;
tr[u].llis = tr[u << 1].llis;
tr[u].rlis = tr[u << 1 | 1].rlis;
tr[u].tlis = max(tr[u << 1].tlis, tr[u << 1 | 1].tlis);
if(w[tr[u << 1].r] < w[tr[u << 1 | 1].l]){ //严格单调增
int mid = tr[u].l + tr[u].r >> 1;
if(tr[u << 1].llis == tr[u << 1].sz) //左边整体严格单调增
tr[u].llis += tr[u << 1 | 1].llis;
if(tr[u << 1 | 1].rlis == tr[u << 1 | 1].sz)
tr[u].rlis += tr[u << 1].rlis;
tr[u].tlis = max(tr[u].tlis, tr[u << 1].rlis + tr[u << 1 | 1].llis);
}
}
void build(int u, int l, int r){
tr[u]= {l, r, 1, 1, 1, 1};
if(l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int x, int v){
if(tr[u].l == tr[u].r){
w[x] = v;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
int query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].tlis;
int mid = tr[u].l + tr[u].r >> 1;
int res = 0;
if(l <= mid) res += query(u << 1, l, mid);
if(r > mid) res += query(u << 1 | 1, mid + 1, r);
return res;
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; ++ i) scanf("%d", &w[i]);
build(1, 1, n);
printf("%d\n", query(1, 1, n));
while(m --){
int x, v;
scanf("%d%d",&x,&v);
modify(1, x, v);
printf("%d\n", query(1, 1, n));
}
return 0;
}Tunnel Warfare 【线段树区间合并】
几乎是模板题,用这个题来开始线段树区间合并再好不过。 待补充Qwq 2021.4.11没写完
注意pushup和query函数, 并不是和最普通的线段树一样的。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<stack>
#define ll long long
#define ls lmax
#define rs rmax
#define ms tmax
#define len sum
#define Tree tr
using namespace std;
const int N = 50010;
int n,m,w[N];
stack<int> distory;
struct node{
int l, r,lmax,rmax,tmax,sum; //维护左边右边全部的连续1的个数,区间长度
}tr[N << 2];
void pushup(int u){
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
tr[u].lmax = tr[u << 1].lmax, tr[u].rmax = tr[u << 1 | 1].rmax, tr[u].tmax = max(tr[u << 1].tmax, tr[u << 1 | 1].tmax);
if(w[tr[u << 1].r] == 1){
if(tr[u << 1].lmax == tr[u << 1].sum) tr[u].lmax += tr[u << 1 | 1].lmax;
if(tr[u << 1 | 1].rmax == tr[u << 1 | 1].sum) tr[u].rmax += tr[u << 1].rmax;
tr[u].tmax = max(tr[u].tmax, tr[u << 1].rmax + tr[u << 1 | 1].lmax);
}
}
void build(int u, int l, int r){
int tmp = r - l + 1;
tr[u] = {l, r, tmp, tmp, tmp, tmp};
if(l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
void modify(int u, int x, int v){
if(tr[u].l == tr[u].r){
w[x] = v;
tr[u] = {tr[u].l, tr[u].r, w[x], w[x], w[x], 1};
}else{
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
}
int query(int u, int x){ //这个函数很重要Qwq 这个x是会动态变化的, 并不是不变的emm
if(tr[u].l == tr[u].r || tr[u].tmax == tr[u].sum || tr[u].tmax == 0) return tr[u].tmax;
if(x <= tr[u << 1].r){ //在左边
if(x >= tr[u << 1].r - tr[u << 1].rmax + 1) return query(u << 1, x) + query(u << 1 | 1, tr[u << 1 | 1].l);
else return query(u << 1, x);
}
if(x >= tr[u << 1 | 1].l){ //在右边
if(x <= tr[u << 1 | 1].lmax + tr[u << 1 | 1].l - 1) return query(u << 1 | 1, x) + query(u << 1, tr[u << 1].r);
else return query(u << 1 | 1, x);
}
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; ++ i) w[i] = 1;
build(1, 1, n);
while(m --){
char opt; int x;
scanf(" %c", &opt);
if(opt == 'D'){
scanf("%d", &x);
modify(1, x, 0);
distory.push(x);
}else if(opt == 'R'){
x = distory.top();
distory.pop();
modify(1, x, 1);
}else{
scanf("%d", &x);
printf("%d\n", query(1, x));
}
}
return 0;
}
LCIS
题意: 求连续最长上升子序列。 注意query的时候不能简单相加, 例如:1 2 5 8 1 2 3 4,查询1->7的区间时,左边的tlics是4,右边的是3,但是答案并不是简单的4 + 3. 所以返回结构体方便书写。
注意ans结构体要赋予l,r的值!
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 100010;
int n,m,t,w[2 * N];
struct node{
int l, r, llcis, rlcis, tlcis, sz;
}tr[N << 2];
void pushup(node& rt, node& ls, node& rs){
rt.llcis = ls.llcis, rt.rlcis = rs.rlcis, rt.tlcis = max(ls.tlcis, rs.tlcis), rt.sz = ls.sz + rs.sz;
if(w[ls.r] < w[rs.l]){
if(ls.tlcis == ls.sz) rt.llcis += rs.llcis;
if(rs.tlcis == rs.sz) rt.rlcis += ls.rlcis;
rt.tlcis = max(rt.tlcis, ls.rlcis + rs.llcis);
}
}
void pushup(int u){
node& rt = tr[u], &ls = tr[u << 1], &rs = tr[u << 1 | 1];
pushup(rt, ls, rs);
}
void build(int u, int l, int r){
tr[u] = {l, r, 1, 1, 1, 1};
if(l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int x, int v){
if(tr[u].l == tr[u].r) w[x] = v;
else{
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
}
void show(node x){
printf("l = %d r = %d tlcis = %d\n", x.l, x.r, x.tlcis);
}
node query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
if(r <= mid) return query(u << 1, l, r);
if(l > mid) return query(u << 1 | 1, l, r);
else{
node n1, n2, ans;
ans.l = tr[u].l, ans.r = tr[u].r;
n1 = query(u << 1, l, r);
n2 = query(u << 1 | 1, l, r);
pushup(ans, n1, n2);
return ans;
}
}
void solve(){
cin >> n >> m;
for(int i = 1; i <= n; ++ i) scanf("%d", &w[i]);
build(1, 1, n);
while(m --){
char op[20];
int x, y;
scanf(" %s%d%d", op, &x, &y); x ++;
if(op[0] == 'Q') y ++, printf("%d\n", query(1, x, y).tlcis);
else modify(1, x, y);
}
}
int main(){
cin >> t;
while(t --) solve();
return 0;
}
Can you answer these queries III
区间最大连续子段和, 注意query返回node节点。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 500010, inf = 1e9 + 7;
int n,m,w[N];
struct node{
int l,r,lmax,rmax,tmax,sum;
}tr[N << 2];
void pushup(node& root, node& ls, node& rs){
root.sum = ls.sum + rs.sum;
root.lmax = max(ls.lmax, ls.sum + rs.lmax);
root.rmax = max(rs.rmax, rs.sum + ls.rmax);
root.tmax = max(max(ls.tmax, rs.tmax), ls.rmax + rs.lmax);
}
void pushup(int u){
node& root = tr[u], &ls = tr[u << 1], &rs = tr[u << 1 | 1];
pushup(root, ls, rs);
}
void build(int u, int l, int r){
tr[u] = {l, r, w[l], w[l], w[l], w[l]};
if(l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int x, int v){
if(tr[u].l == tr[u].r) tr[u] = {x, x, v, v, v, v};
else{
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
}
//node query(int u, int l, int r){ //这样也可以
// if(tr[u].l >= l && tr[u].r <= r) return tr[u];
// int mid = tr[u].l + tr[u].r >> 1;
// if(r <= mid) return query(u << 1, l, r);
// else if(l > mid) return query(u << 1 | 1, l, r);
// else{
// node n1, n2, res;
// n1 = query(u << 1, l, r);
// n2 = query(u << 1 | 1, l, r);
// pushup(res, n1, n2);
// return res;
// }
//}
node query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
node n1 = {0,0,-inf,-inf,-inf,0}, n2 = {0,0,-inf,-inf,-inf,0}, res;
if(l <= mid) n1 = query(u << 1, l, r);
if(r > mid) n2 = query(u << 1 | 1, l, r);
pushup(res, n1, n2);
return res;
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; ++ i) scanf("%d", &w[i]);
build(1, 1, n);
while(m --){
int opt, x, y;
scanf("%d%d%d",&opt,&x,&y);
if(opt == 1){
if(x > y) swap(x, y);
printf("%d\n", query(1,x,y).tmax);
}
else modify(1,x,y);
}
return 0;
}
Hotel---poj3667/洛谷
题意:题意:酒店有n个房间,现有m个团队,每个团队需要连续 d 个房间,现在有两个操作,1:需要 d 个房间,2:从 x 开始连续 d 个房间退房;
当是1的时候,需要d个房间时, 我们尽可能的找到最靠左的房间给客人,输出最左边房间的编号。
线段树区间合并+区间修改。
维护当前区间的连续空床数。
注意query的时候,分成三类:
A. 左边区间连续空床数>=len, 递归往左边查询;
B. 中间区间连续空床数>=len, 答案就是左边区间的坐标(因为要下标最小, 所以直接tr[u << 1].r - tr[u << 1].rsum + 1;
C. 以上不满足,往右边查询。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 50010;
int n,m;
struct node{
int l, r, lsum, rsum, tsum, sz, lazy;
}tr[N];
void pushup(int u){
node& rt = tr[u], &ls = tr[u << 1], &rs = tr[u << 1 | 1];
rt.lsum = ls.lsum, rt.sz = ls.sz + rs.sz, rt.rsum = rs.rsum;
if(ls.tsum == ls.sz) rt.lsum += rs.lsum;
if(rs.tsum == rs.sz) rt.rsum += ls.rsum;
rt.tsum = max(max(ls.tsum, rs.tsum), ls.rsum + rs.lsum);
}
void build(int u, int l, int r){
tr[u] = {l, r, 1, 1, 1, 1, 0};
if(l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void pushdown(int u){
node& rt = tr[u], &ls = tr[u << 1], &rs = tr[u << 1 | 1];
if(rt.lazy == 0) return ;
if(rt.lazy == 1){ // 要住人, 全满
ls.lazy = rs.lazy = 1;
ls.lsum = ls.rsum = ls.tsum = 0;
rs.lsum = rs.rsum = rs.tsum = 0;
}else{ // 要退房, 全空
ls.lazy = rs.lazy = 2;
ls.lsum = ls.rsum = ls.tsum = ls.sz;
rs.lsum = rs.rsum = rs.tsum = rs.sz;
}
rt.lazy = 0;
}
void update(int u, int l, int r, int v){
if(tr[u].l >= l && tr[u].r <= r){
if(v == 1) tr[u].lsum = tr[u].rsum = tr[u].tsum = 0; //要入住
else tr[u].lsum = tr[u].rsum = tr[u].tsum = tr[u].sz; //要退房
tr[u].lazy = v;
}else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) update(u << 1, l, r, v);
if(r > mid) update(u << 1 | 1, l, r, v);
pushup(u);
}
}
int query(int u, int l, int r, int len){
pushdown(u);
if(tr[u].l == tr[u].r) return tr[u].l;
int mid = tr[u].l + tr[u].r >> 1;
if(tr[u << 1].tsum >= len) return query(u << 1, l, r, len);
if(tr[u << 1].rsum + tr[u << 1 | 1].lsum >= len) return tr[u << 1].r - tr[u << 1].rsum + 1;
return query(u << 1 | 1, l, r, len);
}
int main(){
cin >> n >> m;
build(1, 1, n);
while(m --){
int opt, x, y;
//printf("re: %d\n", tr[1].tsum);
scanf("%d%d",&opt,&x);
if(opt == 1){
if(tr[1].tsum >= x){
int tmp = query(1, 1, n, x);
printf("%d\n", tmp);
update(1, tmp, tmp + x - 1, 1);
}else puts("0");
}else{
scanf("%d",&y);
update(1, x, x + y - 1, 2);
}
}
return 0;
}线段树求LIS(权值线段树维护dp的最大值)
896. 最长上升子序列 II
tips:注意要求严格单调增, query函数注意。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 100010;
int n, idx, mx, a[N], ans = 1;
struct node{
int l, r, maxx;
}tr[N << 2];
vector<int> nums;
int pushup(int u){ tr[u].maxx = max(tr[tr[u].l].maxx, tr[tr[u].r].maxx);}
int build(int l, int r){
int p = ++ idx;
if(l == r) return p;
int mid = l + r >> 1;
tr[p].l = build(l, mid), tr[p].r = build(mid + 1, r);
return p;
}
void update(int u, int l, int r, int p, int x){
if(l == r) tr[u].maxx = max(tr[u].maxx, x);
else{
int mid = l + r >> 1;
if(p <= mid) update(tr[u].l, l, mid, p, x);
else update(tr[u].r, mid + 1, r, p, x);
pushup(u);
}
}
int query(int u, int l, int r, int p){
if(p > r) return tr[u].maxx;
if(l == r) return l == p ? 0 : tr[u].maxx;
int mid = l + r >> 1;
int ans = query(tr[u].l, l, mid, p);
if(p > mid) ans = max(ans, query(tr[u].r, mid + 1, r, p));
return ans;
}
int get(int x){ return lower_bound(nums.begin(), nums.end(), x) - nums.begin();}
int main(){
cin >> n;
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
nums.push_back(a[i]);
}
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
mx = nums.size() - 1;
build(0, mx);
for(int i = 1; i <= n; ++ i){
int now = query(1, 0, mx, get(a[i])) + 1;
ans = max(ans, now);
update(1, 0, mx, get(a[i]), now);
}
cout << ans;
return 0;
}LIS
树状数组版:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 100010;
int n,m,tr[N],ans,dp[N];
int lowbit(int x){ return x & (-x);}
int ask(int x){ int res = 0; for(int i = x; i >= 1; i -= lowbit(i)) res = max(res, tr[i]) ; return res; }
void add(int x, int v){ for(int i = x; i <= n; i += lowbit(i)) tr[i] = max(tr[i], v);}
int main(){
cin >> n;
for(int i = 1; i <= n; ++ i){
int x;
scanf("%d", &x);
dp[i] = ask(x - 1) + 1;
add(x, dp[i]);
}
for(int i = 1; i <= n; ++ i) ans = max(ans, dp[i]);
cout << ans;
return 0;
}F Fundraising 【离散化树状数组维护二维带权最长上升子序列】
tips:也可以用离散化权值线段树。
这个题是友好城市 的扩展版,不但数据范围加强,而且带权、不能重叠、需要离散化处理。
思路: 先按照某一个维度排序,这样就可以忽略某一维度的信息了(升序排序,一定可以保证这一维度数据上升), 然后对于两个维度都相同的点,将他们的权值合并; 对于第一维度相同但第二维度不同的点,按照第二维度从大到小排序(这样可以保证第一维度相同的点不会影响答案, 可以画图看看正确性)。然后用树状数组维护即可。
树状数组版:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 100010;
int n,m,mx;
ll w[N],tr[N],ans;
vector<ll> nums;
struct node{
int x, y;
ll w;
}a[N];
int cmp(node n1, node n2){ return n1.x == n2.x ? n1.y > n2.y : n1.x < n2.x;}
int lowbit(int x){ return x & (-x);}
ll ask(int x){
ll res = 0;
for(int i = x - 1; i >= 1; i -= lowbit(i)) res = max(res, tr[i]); //因为要求严格单调,从前一个位置开始查
return res;
}
int add(int x, ll c){for(int i = x; i <= mx; i += lowbit(i)) tr[i] = max(tr[i], c);}
int get(int x){ return lower_bound(nums.begin(), nums.end(), x) - nums.begin();}
int main(){
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ){
scanf("%d%d%lld",&a[i].x, &a[i].y, &a[i].w);
nums.push_back(a[i].y); //离散化第二维度即可, 第一维度是排序关键字
}
sort(a + 1, a + 1 + n, cmp);
nums.push_back(-1); //为了让下标从1开始
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
mx = nums.size();
for(int i = 1; i <= n; ++ i){
if(i < n && a[i].x == a[i + 1].x && a[i].y == a[i + 1].y){ //对于两个维度都相同的点,进行合并
a[i + 1].w += a[i].w;
continue;
}
int id = get(a[i].y);
ll tmp = ask(id) + a[i].w;
ans = max(ans, tmp);
add(id, tmp);
}
cout << ans;
return 0;
}权值线段树版:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 100010;
int n,m,mx,idx;
ll ans;
vector<ll> nums;
struct node{
int x, y;
ll w;
}a[N];
int cmp(node n1, node n2){ return n1.x == n2.x ? n1.y > n2.y : n1.x < n2.x;}
struct Tr{
int l, r;
ll maxx;
}tr[N << 2];
void pushup(int u){tr[u].maxx = max(tr[tr[u].l].maxx, tr[tr[u].r].maxx);}
int build(int l, int r){
int p = ++ idx;
if(l == r) return p;
int mid = l + r >> 1;
tr[p].l = build(l, mid), tr[p].r = build(mid + 1, r);
return p;
}
void modify(int u, int l, int r, int p, ll v){
if(l == r) tr[u].maxx = max(tr[u].maxx, v);
else{
int mid = l + r >> 1;
if(p <= mid) modify(tr[u].l, l, mid, p, v);
else modify(tr[u].r, mid + 1, r, p, v);
pushup(u);
}
}
ll query(int u, int l, int r, int p){
if(p > r) return tr[u].maxx;
if(p == l && p == r) return 0;
int mid = l + r >> 1;
ll ans = query(tr[u].l, l, mid, p);
if(p > mid) ans = max(ans, query(tr[u].r, mid + 1, r, p));
return ans;
}
int get(int x){ return lower_bound(nums.begin(), nums.end(), x) - nums.begin();}
int main(){
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ){
scanf("%d%d%lld",&a[i].x, &a[i].y, &a[i].w);
nums.push_back(a[i].y); //离散化第二维度即可, 第一维度是排序关键字
}
sort(a + 1, a + 1 + n, cmp);
nums.push_back(-1); //为了让下标从1开始
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
mx = nums.size();
build(1, mx);
for(int i = 1; i <= n; ++ i){
if(i < n && a[i].x == a[i + 1].x && a[i].y == a[i + 1].y){ //对于两个维度都相同的点,进行合并
a[i + 1].w += a[i].w;
continue;
}
int id = get(a[i].y);
ll tmp = query(1, 1, mx, id) + a[i].w;
ans = max(ans, tmp);
modify(1, 1, mx, id, tmp);
}
cout << ans;
return 0;
}POJ - 2777 Count Color 【线段树维护区间不同数的个数】
题意:
给出一个序列,一开始都是数字1代表第一种颜色,最多有30种颜色,现在有两种操作:
1 x y col将区间[x,y]的气球都变成颜色col
2 x y 查询区间内不同颜色的气球个数
tips:这个题颜色个数很小,只有30个,可以直接二进制状压表示颜色即可。
将一个区间全部变成某个颜色时,pushdown时直接让左右孩子的颜色等于lazy的颜色即可。
注意query函数并不是简单的相加(可以左右区间的颜色相同)
注意了解:
__builtin_popcount()用于计算一个 32 位无符号整数有多少个位为1
#include<iostream>
#include<algorithm>
#include<cstdio>
#define ll long long
using namespace std;
const int N = 100010;
struct node{
int l, r,color, lazy;
}tr[N << 2];
int n,m,k;
void pushup(int u){ tr[u].color = tr[u << 1].color | tr[u << 1 | 1].color; }
void pushdown(int u){
if(tr[u].lazy){
tr[u << 1].lazy = tr[u << 1 | 1].lazy = tr[u].lazy;
tr[u << 1].color = tr[u << 1 | 1].color = tr[u].lazy;
tr[u].lazy = 0;
}
}
void build(int u, int l, int r){
tr[u] = {l, r, 1, 0};
if(l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void update(int u, int l, int r, int v){
if(tr[u].l >= l && tr[u].r <= r) tr[u].lazy = tr[u].color = 1 << (v - 1);
else{
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if(l <= mid) update(u << 1, l, r, v);
if(r > mid) update(u << 1 | 1, l, r, v);
pushup(u);
}
}
node query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) {
//printf("--l = %d r = %d co = %d\n", tr[u].l, tr[u].r, tr[u].color);
return tr[u];
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
node n1, n2, res;
if(r <= mid) return query(u << 1, l, r);
if(l > mid) return query(u << 1 | 1, l, r);
else{
n1 = query(u << 1, l, r);
n2 = query(u << 1 | 1, l, r);
res.color = n1.color | n2.color;
return res;
}
}
int main(){
cin >> n >> k >> m;
build(1, 1, n);
while (m -- ){
char op; int x, y, z;
scanf(" %c%d%d",&op,&x,&y);
if(x > y) swap(x, y);
if(op == 'C'){
scanf("%d", &z);
update(1, x, y, z);
//printf("--%d\n", query(1, x, y));
}else printf("%d\n", __builtin_popcount(query(1, x, y).color));
}
return 0;
}Codeforces Beta Round #43 D. Parking Lot
题意: 有一条长度为L的街道,有N个操作,操作有两种,(1)"1 a",表示有一辆长度为a的车开进来想找停车位,停车位必须满足与它前面的车距离至少为b,与后面的车距离至少为f.如果能找到这样的停车位,输出这辆车的起始位置(且这个位置最小),否则输出-1。(2)"2 a",表示第a个事件里进来停车的那辆车开出去了。
tips:当一辆车停进来的时候,要满足前后的间距要求;但是这辆车停下,下一辆车停好后,可能不满足前一辆车的间距要求了! 例如b = 1, f = 2,前一辆车所在区间是[3,6],下一辆车可以从7开始停车(只需要满足7 - b >= 6即可)。
注意最开始的位置不需要满足与后面间隔是b的条件, 而最后一辆车也不需要满足和前面间隔是f的条件(这一点可以通过建立n + b + f长度的线段树来解决)。询问的时候询问len + b + f, 而修改的时候只修改len(b,f只是间隔)
注意query函数, 其他就是和hotel一样的写法。
#include<bits/stdc++.h>
#define PII pair<int, int>
#define x first
#define y second
#define ll long long
using namespace std;
const int N = 100210;
int n,m,b,f,car;
PII park[N];
struct node{
int l, r, lsum, rsum, tsum, sz, lazy;
}tr[N << 2];
void pushup(int u){
node& rt = tr[u], &ls = tr[u << 1], &rs = tr[u << 1 | 1];
rt.lsum = ls.lsum, rt.rsum = rs.rsum, rt.sz = ls.sz + rs.sz;
if(ls.tsum == ls.sz) rt.lsum += rs.lsum;
if(rs.tsum == rs.sz) rt.rsum += ls.rsum;
rt.tsum = max(max(ls.tsum, rs.tsum), ls.rsum + rs.lsum);
}
void build(int u, int l, int r){
tr[u] = {l, r, 1, 1, 1, 1, 0};
if(l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void pushdown(int u){
node& rt = tr[u], &ls = tr[u << 1], &rs = tr[u << 1 | 1];
if(rt.lazy){
ls.lazy = rt.lazy, rs.lazy = rt.lazy;
if(rt.lazy == 1){ //parking
ls.lsum = ls.rsum = ls.tsum = 0;
rs.lsum = rs.rsum = rs.tsum = 0;
}else{ //leaving
ls.lsum = ls.rsum = ls.tsum = ls.sz;
rs.lsum = rs.rsum = rs.tsum = rs.sz;
}
rt.lazy = 0;
}
}
void update(int u, int l, int r, int v){
if(tr[u].l >= l && tr[u].r <= r){
tr[u].lazy = v;
if(v == 1) tr[u].lsum = tr[u].rsum = tr[u].tsum = 0;
else tr[u].lsum = tr[u].rsum = tr[u].tsum = tr[u].sz;
}else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) update(u << 1, l, r, v);
if(r > mid) update(u << 1 | 1, l, r, v);
pushup(u);
}
}
int query(int u, int l, int r, int len){
pushdown(u);
if(tr[u].l == 1 && tr[u].lsum + b >= len) return 1; //最头上不需要管b的间隔
if(tr[u].l == tr[u].r) return tr[u].l;
int mid = tr[u].l + tr[u].r >> 1;
if(tr[u << 1].tsum >= len) return query(u << 1, l, r, len);
if(tr[u << 1].rsum + tr[u << 1 | 1].lsum >= len) return tr[u << 1].r - tr[u << 1].rsum + 1;
return query(u << 1 | 1, l, r, len);
}
int main(){
cin >> n >> b >> f;
build(1, 1, n + b + f);
cin >> m;
for(int z = 1; z <= m; ++ z){
int opt, x;
scanf("%d%d",&opt,&x);
if(opt == 1){
if(tr[1].tsum < x + b + f) puts("-1");
else{
int tmp = query(1, 1, n + b + f, x + b + f);
if(tmp != 1) tmp += b;
if(tmp + x - 1 > n){
puts("-1");
continue ;
}
park[z] = {tmp - 1, tmp + x};
printf("%d\n", tmp - 1);
update(1, tmp, tmp + x - 1, 1);
}
}else{
PII now = park[x];
update(1, now.x, now.y, 2);
}
}
return 0;
}hdu3397 Sequence operation 【毒瘤】
题意: 有N个为0或为1的数,有M个操作,操作有5种类型。(1)“0 a b”,表示将区间[a,b]范围内的数全部置0。(2)“1 a b”,表示将区间[a,b]内的数全部置1。(3)"2 a b",表示将区间[a,b]内的数0变成1,1变成0。(4)"3ab",表示查询[a,b]范围内1的数。(5)"4 a b",表示查询[a,b]范围内最长的连续的1。
思路:区间合并的综合应用。
维护左边、右边、总体连续的1、0,1、0的个数等。
注意如果某个点有reverse标记,然后上面下传了lazy,那就把这个reverse去掉(这里非常重要。如果不去,下面那个操作的pushdown可能会因为这里的reverse而超时),因为一定会被全部变成lazy,没有必要翻转了;如果某个点有lazy标记,上面下传了reverese,则先pushdown(ls,rs),把这个子树的lazy标记全部下放(如果孩子的孩子也有lazy,那就继续下放),再reverse,这样可以保证不错(否则lazy和reverse会乱)。
然后就是常规的区间合并。
#include<iostream>
#include<algorithm>
#include<cstdio>
#define ll long long
using namespace std;
const int N = 100010;
struct node{
int l, r, sz, sz1, sz0, llen1, llen0, rlen1, rlen0, tlen1, tlen0, lazy, reverse;
}tr[N * 6];
int n,w[N],m,t;
void pushup(node& rt, node& ls, node& rs){
rt.sz = ls.sz + rs.sz, rt.sz1 = ls.sz1 + rs.sz1, rt.sz0 = ls.sz0 + rs.sz0;
rt.llen1 = ls.llen1, rt.llen0 = ls.llen0, rt.rlen1 = rs.rlen1, rt.rlen0 = rs.rlen0;
rt.tlen1 = max(max(ls.tlen1, rs.tlen1), ls.rlen1 + rs.llen1), rt.tlen0 = max(max(ls.tlen0, rs.tlen0), ls.rlen0 + rs.llen0);
if(ls.tlen1 == ls.sz) rt.llen1 += rs.llen1;
if(ls.tlen0 == ls.sz) rt.llen0 += rs.llen0;
if(rs.tlen1 == rs.sz) rt.rlen1 += ls.rlen1;
if(rs.tlen0 == rs.sz) rt.rlen0 += ls.rlen0;
}
void pushup(int u){ pushup(tr[u], tr[u << 1], tr[u << 1 | 1]); }
void build(int u, int l, int r){
tr[u] = {l, r, 1, w[l] == 1, w[l] == 0, w[l] == 1, w[l] == 0, w[l] == 1, w[l] == 0, w[l] == 1, w[l] == 0, -1, 0};
if(l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void Swap(node& rt){ swap(rt.llen0, rt.llen1), swap(rt.rlen1, rt.rlen0), swap(rt.tlen0, rt.tlen1);swap(rt.sz0, rt.sz1);}
void pushdown(int u){
node& rt = tr[u], &ls = tr[u << 1], &rs = tr[u << 1 | 1];
if(rt.l == rt.r) return ;
if(rt.reverse){
pushdown(u << 1); pushdown(u << 1 | 1);
Swap(ls); Swap(rs);
ls.reverse ^= 1, rs.reverse ^= 1;
rt.reverse = 0;
}
if(rt.lazy != -1){
ls.reverse = rs.reverse = 0;
if(rt.lazy == 1){ //全变成1
ls.lazy = rs.lazy = 1;
ls.sz1 = ls.sz, rs.sz1 = rs.sz, ls.sz0 = rs.sz0 = 0;
ls.llen1 = ls.rlen1 = ls.tlen1 = ls.sz;
ls.llen0 = ls.rlen0 = ls.tlen0 = 0;
rs.llen1 = rs.rlen1 = rs.tlen1 = rs.sz;
rs.llen0 = rs.rlen0 = rs.tlen0 = 0;
}else if(rt.lazy == 0){ //全变成0
ls.lazy = rs.lazy = 0;
ls.sz0 = ls.sz, rs.sz0 = rs.sz, ls.sz1 = rs.sz1 = 0;
ls.llen0 = ls.rlen0 = ls.tlen0 = ls.sz;
ls.llen1 = ls.rlen1 = ls.tlen1 = 0;
rs.llen0 = rs.rlen0 = rs.tlen0 = rs.sz;
rs.llen1 = rs.rlen1 = rs.tlen1 = 0;
}
rt.lazy = -1;
}
}
void update(int u, int l, int r, int v){
node& rt = tr[u], &ls = tr[u << 1], &rs = tr[u << 1 | 1];
if(tr[u].l >= l && tr[u].r <= r){
if(v == 1) rt.lazy = 1, rt.reverse = 0, rt.sz1 = rt.sz, rt.sz0 = 0, rt.llen1 = rt.rlen1 = rt.tlen1 = rt.sz, rt.llen0 = rt.rlen0 = rt.tlen0 = 0;
else if(v == 0) rt.lazy = 0, rt.reverse = 0, rt.sz0 = rt.sz, rt.sz1 = 0, rt.llen0 = rt.rlen0 = rt.tlen0 = rt.sz, rt.llen1 = rt.rlen1 = rt.tlen1 = 0;
else{
if(rt.lazy != -1) rt.lazy ^= 1;
rt.reverse ^= 1;
Swap(rt);
}
}else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) update(u << 1, l, r, v);
if(r > mid) update(u << 1 | 1, l, r, v);
pushup(u);
}
}
node query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u];
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(r <= mid) return query(u << 1, l, r);
else if(l > mid) return query(u << 1 | 1, l, r);
else{
node n1 = query(u << 1, l, r);
node n2 = query(u << 1 | 1, l, r);
node res = {l,r,0,0,0,0,0,0,0,0,0,-1,0}; pushup(res, n1, n2);
return res;
}
}
int main(){
cin >> t;
while(t --){
cin >> n >> m;
for(int i = 1; i <= n; ++ i) scanf("%d",&w[i]);
build(1, 1, n);
while(m --){
int op, x, y;
scanf("%d%d%d",&op,&x,&y); x ++, y ++;
if(op == 0) update(1, x, y, 0);
else if(op == 1) update(1, x, y, 1);
else if(op == 2) update(1, x, y, 2);
else if(op == 3) printf("%d\n", query(1, x, y).sz1);
else{
node n1 = query(1, x, y);
printf("%d\n", n1.tlen1);
}
}
}
return 0;
}

京公网安备 11010502036488号