第三周 线段树入门

文章目录

    • 内容概括
      • 涉及算法
        • 线段树 区间更新 查询
          • ZOJ1610 线段树+延迟更新 区间更新+区间查询(延迟标记)(简单)
          • HDU4027 线段树 唯一修改(区间开根) 暴力区间单点更新+区间查询
          • HDU1540 线段树应用 单点更新+区间最值查询
          • HDU3974 dfs序+线段树 区间更新+单点查询
          • HDU4578 区间更新+区间查询(初始都为0)
          • hdu4614 线段树 延迟标记 二分 (未给定确定区间)

内容概括

涉及算法

线段树 区间更新 查询

ZOJ1610 线段树+延迟更新 区间更新+区间查询(延迟标记)(简单)

题意:给n次区间染色,问最终出现过的颜色连续段的次数

题解:注意区间以及染色的范围都是[0,8000],不是[0,n].以及维护的是区间长度,而不是区间,用每个点去表示该点被某种颜色覆盖,即[a,b]区间染色转化为[a+1,b]所有的点染色. 延迟更新:为了保证正确地记录连续段,将所有拥有染色节点的节点标记为-2,表示其下面节点有染色节点,保证了不同颜色的连续段的正确更新(因为有的节点可能并未染色,此时为-1)

//#include

#include 
#include 
#include 
using namespace std;
typedef long long ll;

const int MAXN = 8e3+5;
int n,il,ir,c;
int tree[MAXN<<2];
int vis[MAXN],cnt[MAXN];
void update(int o,int l,int r,int tl,int tr,int k){
	if (tl > r || tr < l) return ;
	if (l >= tl && r <= tr){
		tree[o] = k;
		return ;
	}
	if (tree[o] > -1){
		tree[o<<1] = tree[o<<1|1] = tree[o];
		tree[o] = -2;
	}
	int mid = (l+r)>>1;
	if (tl <= mid) update(o<<1,l,mid,tl,tr,k);
	if (tr > mid) update(o<<1|1,mid+1,r,tl,tr,k);
	tree[o] = -2;
}
int tmp = -1;
void query(int o,int l,int r){
	if (tree[o] == -1) {
		tmp = -1;
		return ;
	}
	if (tree[o]!=-2){
		if (tree[o]!=tmp){
			cnt[tree[o]]++;
			tmp = tree[o];
		}
		
		return ;
	}
	if (l == r) return ;
	
	int mid = (l+r)>>1;
	query(o<<1,l,mid);
	query(o<<1|1,mid+1,r);
}
int main(){
	while (scanf("%d",&n)!=EOF){
		memset(tree,-1,sizeof tree);
		memset(cnt,0,sizeof cnt);
		int MAXC = -1;
		for (int i = 0; i < n; ++i){
			scanf("%d%d%d",&il,&ir,&c);
			if (il >= ir) continue;
			update(1,1,8000,il+1,ir,c);
			if (c > MAXC) MAXC = c; 
		}
		tmp = -1;
		query(1,1,8000);
		for (int i = 0; i <= MAXC; ++i){
			if (cnt[i]){
				printf("%d %d\n",i,cnt[i]);
			}
		}
		printf("\n");
	}
    return 0;
}

HDU4027 线段树 唯一修改(区间开根) 暴力区间单点更新+区间查询

题解:因为只有开根操作,且最大范围在2^{63}内,因此每个数最多开7次根左右。单点更新时剪枝即可(区间长度等于区间和)


#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;

const int MAXN = 1e5+5;
ll tree[MAXN<<2];

void build(int o,int l,int r){
	if (l == r){
		scanf("%lld",&tree[o]);
		return ;
	}
	int mid = (l+r)>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	tree[o] = tree[o<<1] + tree[o<<1|1];
}

void update(int o,int l,int r,int tl,int tr){
	if (tl > r || tr < l) return ;
	if (tree[o] == r-l+1 && tl <= l && tr >= r) return ;
	if (l == r){
		tree[o] = sqrt(tree[o]*1.0);
		return ;
	}
	int mid = (l+r)>>1;
	if (tl <= mid) update(o<<1,l,mid,tl,tr);
	if (tr > mid) update(o<<1|1,mid+1,r,tl,tr);
	tree[o] = tree[o<<1] + tree[o<<1|1];
}
ll ans;
void query(int o,int l,int r,int tl,int tr){
	if (tl > r || tr < l) return ;
	if (l >= tl && r <= tr) {
		ans += tree[o];
		return ;
	}
	int mid = (l+r)>>1;
	if (tl <= mid) query(o<<1,l,mid,tl,tr);
	if (tr > mid) query(o<<1|1,mid+1,r,tl,tr);
}

int main(){
	int n,m;
	int t = 1;
	while (scanf("%d",&n)!=EOF){
		build(1,1,n);
		scanf("%d",&m);printf("Case #%d:\n",t++);
		int x,y,z;
		while (m--){
			scanf("%d%d%d",&x,&y,&z);
			
			if(y > z) swap(y,z);
			if (x == 0){
				update(1,1,n,y,z);
			}else{
				ans = 0;
				query(1,1,n,y,z);
				printf("%lld\n",ans);
			}
		}
		printf("\n");	
	}
	 
    return 0;
}

HDU1540 线段树应用 单点更新+区间最值查询

题解:可以直接维护破坏的位置的最值,每次询问直接询问目标位置左边的最大值和右边的最小值。差值即为连续的长度。特判该点被破坏时为0。边界处理则把一开始将不存在的边界破坏掉。

//#include

#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 5e4+5;
int ma[MAXN<<2],mi[MAXN<<2];
int pos[MAXN];
int k = 0,n;
void update(int o,int l,int r,int x,int f){
	if (l == r){
		if (f == 1){
			ma[o] = -1;
			mi[o] = INF;
		}else ma[o] = mi[o] = l;
		return ;
	}
	int mid = (l+r)>>1;
	if (x <= mid) update(o<<1,l,mid,x,f);
	else update(o<<1|1,mid+1,r,x,f);
	ma[o] = max(ma[o<<1],ma[o<<1|1]);
	mi[o] = min(mi[o<<1],mi[o<<1|1]);
}
int mx,mn;
void query(int o,int l,int r,int tl,int tr,int f){
	if (tl > r || tr < l) return ;
	if (tl <= l && tr >= r){
		if (f == 0){
			mn = min(mn,mi[o]);
		}else mx = max(mx,ma[o]);
		return ;
	}
	int mid = (l+r)>>1;
	if (tl <= mid) query(o<<1,l,mid,tl,tr,f);
	if (tr > mid) query(o<<1|1,mid+1,r,tl,tr,f);
}
int main(){
	int m;
	while(scanf("%d%d",&n,&m)!=EOF){
		memset(ma,-1,sizeof(ma));
		memset(mi,0x3f,sizeof(mi));
		update(1,0,n+1,0,0);
		update(1,0,n+1,n+1,0);
		k = 0;
		while (m--){
			char st[3];
			int x;
			scanf("%s",st);
			if (st[0] == 'D')
			{
				scanf("%d",&x);
				update(1,0,n+1,x,0);
				pos[++k] = x;
			}
			else if (st[0] == 'Q')
			{
				mx = -1,mn = INF;
				scanf("%d",&x);
				query(1,0,n+1,x,n+1,0);
				query(1,0,n+1,0,x,1);
				if (mx == mn){
					printf("0\n");
				}else printf("%d\n",mn-mx-1);
			}
			else if (st[0] == 'R')
			{
				update(1,0,n+1,pos[k--],1);
			}
		}
	}
	
    return 0;
}

HDU3974 dfs序+线段树 区间更新+单点查询

题解:跑出dfs序区间更新即可。更新查询时注意pushdown.


#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 5e4+5;
vector  G[MAXN];

int lid[MAXN];
int rid[MAXN];
int in[MAXN];
int tree[MAXN<<2];
void dfs(int v,int p,int &k){
	++k;
	lid[v] = k;
	for (int i = 0; i < G[v].size(); ++i){
		if (G[v][i] != p){
			dfs(G[v][i],v,k);	
		}
	}
	rid[v] = k;
}

void update(int o,int l,int r,int tl,int tr,int kx){
	if (tl > r || tr < l) return ;
	if (tl <= l && tr >= r){
		tree[o] = kx;
		return ;
	}
	if (tree[o] != -1){
		tree[o<<1] = tree[o<<1|1] = tree[o];
		tree[o] = -1;
	}
	int mid = (l+r)>>1;
	if (tl <= mid) update(o<<1,l,mid,tl,tr,kx);
	if (tr > mid) update(o<<1|1,mid+1,r,tl,tr,kx);
}
int ans = -1;
void query(int o,int l,int r,int tl){
	if (l == r && l == tl){
		if (tree[o]!=-1)
		ans = tree[o];
		return ;
	}
	if (tree[o] != -1){
		tree[o<<1] = tree[o<<1|1] = tree[o];
		tree[o] = -1;
	}
	int mid = (l+r)>>1;
	if (tl > mid) query(o<<1|1,mid+1,r,tl);
	else if (tl <= mid) query(o<<1,l,mid,tl);
}
int main(){
	int n;
	int t,Case = 1;
	scanf("%d",&t);
	while (t--){
		scanf("%d",&n);
		memset(tree,-1,sizeof(tree));
		memset(in,0,sizeof(in));
		memset(lid,0,sizeof(lid));
		memset(rid,0,sizeof(rid));
		for (int i = 1; i <= n; ++i) G[i].clear();
		int u,v;
		for (int i = 0; i < n-1; ++i){
			scanf("%d%d",&u,&v);
			G[v].push_back(u);
			in[u]++;
		}
		printf("Case #%d:\n",Case++);
		int k = 0;
		for (int i = 1; i <= n; ++i){
			if (in[i] == 0){
				dfs(i,-1,k);
			}
		}
		int m;
		scanf("%d",&m);
		while (m--){
			char st[2];
			int x,y;
			scanf("%s",st);
			if (st[0] == 'C'){
				scanf("%d",&x);
				ans = -1;
				query(1,1,k,lid[x]);
				printf("%d\n",ans);
			}else {
				scanf("%d%d",&x,&y);
				update(1,1,k,lid[x],rid[x],y);
			}
		}
	}
    return 0;
}

HDU4578 区间更新+区间查询(初始都为0)

题解:因为初始值都为0且有区间覆盖操作,可以维护相同数的区间,注意区间数值覆盖时对标记进行上推,否则查询时间复杂度会越来越高。每次懒标记向下压时标记该节点仍有不同的子节点(我用-1标记,初始为0),对每次加乘更新或者查询操作判断只要不是-1即可进行访问。每次更新注意如果是覆盖操作就把标记往上推(对一个节点的两个子节点判断相同且未被标记就更新该节点的值为其中一个子节点的值)。

另一种思路是直接维护对p = 1,2,3时的区间和。写法比较复杂。

//#include
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e5+5;
const ll mod = 1E4+7;
int n,m,ans;
ll tree[MAXN<<2];
ll lazy[MAXN<<2];
void update(int o,int l,int r,int tl,int tr,ll k,int op){
	if (tl > r || tr < l) return ;
	if (tl <= l && tr >= r && tree[o] != -1 && op != 3){
		if (op == 1) tree[o] = (tree[o]%mod + k%mod)%mod;
		else if (op == 2) tree[o] = (tree[o]%mod * k%mod)%mod;
		return ;
	}
	if (tl <= l && tr >= r && op == 3){
		tree[o] = k;
		return ;
	}
	if (tree[o] >= 0){
		tree[o<<1] = tree[o<<1|1] = tree[o];
		tree[o] = -1;
	}
	int mid = (l + r)>>1;
	if (tl <= mid) update(o<<1,l,mid,tl,tr,k,op);
	if (tr > mid) update(o<<1|1,mid+1,r,tl,tr,k,op); 
	if (tree[o<<1] == tree[o<<1|1] && tree[o<<1] >= 0) tree[o] = tree[o<<1];
}

void query(int o,int l,int r,int tl,int tr,int k){
	if (tl > r || tr < l) return ;
	if (tl <= l && tr >= r && tree[o] >= 0){
		ll tmp = 1;
		for (int i = 1; i <= k; ++i){
			tmp = tmp%mod * tree[o]%mod;
			tmp = tmp%mod;
		}
		ans = ans%mod + (tmp%mod * (r - l + 1)%mod)%mod;
		ans = ans % mod;
		return ;
	}
	if (l == r) return ;
	if (tree[o] >= 0){
		tree[o<<1] = tree[o<<1|1] = tree[o];
		tree[o] = -1;
	}
	
	int mid = (l + r)>>1;
	if (tl <= mid) query(o<<1,l,mid,tl,tr,k);
	if (tr > mid) query(o<<1|1,mid+1,r,tl,tr,k); 
	
}

int main(){
	while (scanf("%d%d",&n,&m)&&n&&m){
		memset(tree,0,sizeof tree);
		for (int i = 1; i <= 4*n; ++i) lazy[i] = 0;
		while (m--)
		{
			int op,x,y;
			ll z;
			scanf("%d%d%d%lld",&op,&x,&y,&z);
			if (op >= 1 && op <= 3){
				update(1,1,n,x,y,z,op);
			/*	for (int i = 1; i <= 4*n; ++i){
					printf("%lld ",tree[i]);
				}puts("");*/
			}else{
				ans = 0;
				query(1,1,n,x,y,z);
				printf("%lld\n",ans);
			}
		} 
	}	
    return 0;
}

维护区间和 懒标记 由于有三种混合操作,且询问有三种格式(同时维护).更好的理解延迟标记。

先分三种情况分别存p = 1,2,3的的区间和。每次更新记录标记的类型(为乘法或加法或覆盖),注意乘法要同时维护加法标记(val(加法标记)*val(乘法标记))。覆盖操作则将该区间其他标记初始化。

对每次更新:

若为覆盖操作,覆盖标记置为当前更新值,区间值更改为 区间长度*更新值 ,其他标记初始化。

若为加法操作,该节点表示的区间值为 当前节点值 + 更新值 。 对于 p = 2,3的情况,即(a + c)^ 2 与 (a + c)^ 3

a为当前节点表示的区间和的值,c为更新值,将上面的式子分别展开,可以发现p2可以由p1更新而来,同理p3可由p2和p1更新得到。 注意更新三者的顺序,不能让更新后的p1影响到p2,p3等,所以先更新p3,再p2,p1.

若为乘法操作,即(a * c)^ 1,(a * c)^ 2,(a * c)^ 3直接乘c的次方即可,注意将加法的标记更新。(因为往下pushdown是用set的标记来更新的)。

延迟更新操作注意左右节点和父节点的标记更新。其他按左右节点的区间进行更新即可。

#include 
using namespace std;

typedef long long           ll;

const double  EPS  = 1e-8;
const int     INF  = 0x3f3f3f3f;
const int     mod  = 10007;
const int     MAXN = (int)1e5 + 5;
int n,m;
struct node{
	int p[4];//存放该区间的和 
	int add,mul,set; //标记为加或乘的常数 
}tree[MAXN<<2];

void pushup(int o){
	for (int i = 1; i <= 3; ++i){
		tree[o].p[i] = (tree[o<<1].p[i]%mod + tree[o<<1|1].p[i]%mod)%mod;
	}
} 

void pushdown(int o,int l,int r){
	int mid = (l+r)>>1;
	if (tree[o].set){
		int mk = tree[o].set;
		tree[o<<1].add = tree[o<<1|1].add = 0;
		tree[o<<1].mul = tree[o<<1|1].mul = 1;
		tree[o<<1].set = tree[o<<1|1].set = mk;
		tree[o].set = 0;
		
		tree[o<<1].p[1] = ((mid-l+1)%mod * mk%mod)%mod; 
		tree[o<<1|1].p[1] = ((r-mid)%mod * mk%mod)%mod;
		
		tree[o<<1].p[2] = ((mid-l+1)%mod * mk%mod * mk%mod)%mod; 
		tree[o<<1|1].p[2] = ((r-mid)%mod * mk%mod * mk%mod)%mod; 
		
		tree[o<<1].p[3] = ((mid-l+1)%mod * mk%mod * mk%mod * mk%mod)%mod; 
		tree[o<<1|1].p[3] = ((r - mid)%mod * mk%mod * mk%mod * mk%mod)%mod; 
	}
	if (tree[o].mul != 1){
		int mk = tree[o].mul;
		
		tree[o<<1].add = (tree[o<<1].add%mod * mk%mod)%mod; 
		tree[o<<1|1].add = (tree[o<<1|1].add%mod * mk%mod)%mod;
		
		tree[o<<1].mul = (tree[o<<1].mul%mod * mk%mod)%mod; 
		tree[o<<1|1].mul = (tree[o<<1|1].mul%mod * mk%mod)%mod;
		
		tree[o<<1].p[1] = (tree[o<<1].p[1]%mod * mk%mod)%mod;
		tree[o<<1].p[2] = (tree[o<<1].p[2]%mod * mk%mod * mk%mod)%mod;
		tree[o<<1].p[3] = (tree[o<<1].p[3]%mod * mk%mod * mk%mod * mk%mod)%mod;
		
		tree[o<<1|1].p[1] = (tree[o<<1|1].p[1]%mod * mk%mod)%mod;
		tree[o<<1|1].p[2] = (tree[o<<1|1].p[2]%mod * mk%mod * mk%mod)%mod;
		tree[o<<1|1].p[3] = (tree[o<<1|1].p[3]%mod * mk%mod * mk%mod * mk%mod)%mod;
		tree[o].mul = 1;
	}
	if (tree[o].add){
		
		int mk = tree[o].add;
		tree[o<<1].add = (tree[o<<1].add%mod + mk%mod)%mod; 
		tree[o<<1|1].add = (tree[o<<1|1].add%mod + mk%mod)%mod;
		
		tree[o<<1].p[3] = (tree[o<<1].p[3]%mod + ((mid-l+1)%mod * mk%mod * mk%mod * mk%mod)%mod + (3 * tree[o<<1].p[2]%mod * mk%mod)%mod + (3 * mk%mod * mk%mod * tree[o<<1].p[1]%mod)%mod)%mod;
		tree[o<<1].p[2] = (tree[o<<1].p[2]%mod + ((mid-l+1)%mod * mk%mod * mk%mod)%mod + (2 * tree[o<<1].p[1]%mod * mk%mod)%mod)%mod;
		tree[o<<1].p[1] = (tree[o<<1].p[1]%mod + ((mid-l+1)%mod * mk%mod)%mod)%mod;
		
		tree[o<<1|1].p[3] = (tree[o<<1|1].p[3]%mod + ((r-mid)%mod * mk%mod * mk%mod * mk%mod)%mod + (3 * tree[o<<1|1].p[2]%mod * mk%mod)%mod + (3 * mk%mod * mk%mod * tree[o<<1|1].p[1]%mod)%mod)%mod;
		tree[o<<1|1].p[2] = (tree[o<<1|1].p[2]%mod + ((r-mid)%mod * mk%mod * mk%mod)%mod + (2 * tree[o<<1|1].p[1]%mod * mk%mod)%mod)%mod;
		tree[o<<1|1].p[1] = (tree[o<<1|1].p[1]%mod + ((r-mid)%mod * mk%mod)%mod)%mod;
		tree[o].add = 0;
	}
}

void update(int o,int l,int r,int tl,int tr,int c,int op){
	if (tl > r || tr < l) return ;
	if (tl <= l && tr >= r){
		if (op == 3){
	    	tree[o].p[1] = ((r-l+1)%mod * c%mod)%mod;
	    	tree[o].p[2] = ((r-l+1)%mod * c%mod * c%mod)%mod;
	    	tree[o].p[3] = ((r-l+1)%mod * c%mod * c%mod * c%mod)%mod;
			tree[o].set = c;
			tree[o].mul = 1;
			tree[o].add = 0;	
		}else if (op == 2){
			tree[o].p[1] = (tree[o].p[1]%mod * c%mod)%mod;
			tree[o].p[2] = (tree[o].p[2]%mod * c%mod * c%mod)%mod;
			tree[o].p[3] = (tree[o].p[3]%mod * c%mod * c%mod * c%mod)%mod;
			tree[o].mul = (tree[o].mul%mod * c%mod)%mod;
			tree[o].add = (tree[o].add%mod * c%mod)%mod;
		}else if (op == 1){
			tree[o].p[3] = (tree[o].p[3]%mod + ((r-l+1)%mod * c%mod * c%mod * c%mod)%mod+ (3 * tree[o].p[2]%mod * c%mod)%mod + (3 * c%mod * c%mod * tree[o].p[1]%mod)%mod)%mod;
			tree[o].p[2] = (tree[o].p[2]%mod + ((r-l+1)%mod * c%mod * c%mod)%mod + (2 * tree[o].p[1]%mod * c%mod)%mod)%mod;
			tree[o].p[1] = (tree[o].p[1]%mod + ((r-l+1)%mod * c%mod)%mod)%mod;
			tree[o].add = (tree[o].add%mod + c%mod)%mod; 
		}
		return ;
	}
	pushdown(o,l,r);
	int mid = (l+r)>>1;
	if (tl <= mid) update(o<<1,l,mid,tl,tr,c,op);
	if (tr > mid) update(o<<1|1,mid+1,r,tl,tr,c,op);
	pushup(o);
}
int ans;
void query(int o,int l,int r,int tl,int tr,int c){
	if (tl > r || tr < l) return ;
	if (tl <= l && tr >= r){
		ans = (ans + tree[o].p[c]%mod)%mod;
		return ;
	}
	pushdown(o,l,r);
	int mid = (l+r)>>1;
	if (tl <= mid) query(o<<1,l,mid,tl,tr,c);
	if (tr > mid) query(o<<1|1,mid+1,r,tl,tr,c);
}

int main() {
	while(scanf("%d%d",&n,&m),n&&m){
		for (int i = 1; i <= (n<<2); ++i) {
			tree[i].set = tree[i].add = 0;
			memset(tree[i].p,0,sizeof tree[i].p);
			tree[i].mul = 1;
		}
		int x,y,op;int z;
		while (m--){
			scanf("%d%d%d%d",&op,&x,&y,&z);
			if (op >= 1 && op <= 3) update(1,1,n,x,y,z,op);
			else{
				ans = 0;
				query(1,1,n,x,y,z);
				printf("%d\n",ans);
			}
		}
		
	}	
	return 0;
}

hdu4614 线段树 延迟标记 二分 (未给定确定区间)

题解:维护空瓶子的数量和区间空瓶子最左端的位置和最右端的位置,查询时可以先查询最左最右的空瓶子,输出这个位置,再进行更新。用1表示空瓶子,维护区间和,查询区间花的数目时可以用区间长度减去这段区间空瓶子的数目。

在放F朵花时,二分右区间,查找空瓶子区间和来确定右区间。

#include 
using namespace std;

typedef long long           ll;

const double  EPS  = 1e-8;
const int     INF  = 0x3f3f3f3f;
const int     mod  = 10007;
const int     MAXN = (int)5e4 + 5;
int n,m,ft,lt;
struct node{
	int last,first;//维护最左端空瓶和最右端空瓶 
	int sum,lazy;
}tree[MAXN<<2];
void build(int o,int l,int r){
	tree[o].sum = r-l+1;
	tree[o].last = r;
	tree[o].first = l;
	tree[o].lazy = 0;
	if (l == r) return ;
	int mid = (l+r)>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
}
void pushdown(int o,int l,int r){ 
	int mid = (l+r)>>1;
	if (tree[o].lazy != 0){
		if (tree[o].lazy == 1)
		{
			tree[o<<1].sum =mid-l+1;
			tree[o<<1|1].sum = r-mid;
			
			tree[o<<1].first = l; tree[o<<1].last = mid;
			
			tree[o<<1|1].first = mid+1; tree[o<<1|1].last = r;
			
			tree[o<<1].lazy = tree[o<<1|1].lazy = 1;
		}else if (tree[o].lazy == -1)
		{
			tree[o<<1].sum = 0;
			tree[o<<1|1].sum = 0;
			tree[o<<1].first = -1; tree[o<<1].last = -1;
			tree[o<<1|1].first = -1; tree[o<<1|1].last = -1;
			tree[o<<1].lazy = tree[o<<1|1].lazy = -1;
		}
		tree[o].lazy = 0;
	}
}
void pushup(int o){
	tree[o].sum = tree[o<<1].sum + tree[o<<1|1].sum;
	if (tree[o<<1].first != -1) tree[o].first = tree[o<<1].first;
	else tree[o].first = tree[o<<1|1].first;
	if (tree[o<<1|1].last != -1) tree[o].last = tree[o<<1|1].last;
	else tree[o].last = tree[o<<1].last; 
}
void update(int o,int l,int r,int tl,int tr,int op){
	if (tl > r || tr < l) return ;
	if (tl <= l && tr >= r){
		if (op == 1){ //清空 
			if (tree[o].sum == r-l+1) return ;
			tree[o].sum = r-l+1;
			tree[o].last = r;
			tree[o].first = l;
			tree[o].lazy = 1;
			return ;
		}else{
			if (tree[o].sum == 0) return ;
			tree[o].sum = 0;
			tree[o].first = -1;
			tree[o].last = -1;
			tree[o].lazy = -1;
			return ;
		}
	}

	pushdown(o,l,r);
	int mid = (l+r)>>1;
	if (tl <= mid) update(o<<1,l,mid,tl,tr,op);
	if (tr > mid) update(o<<1|1,mid+1,r,tl,tr,op);
	pushup(o);
}
int an;
void q_sum(int o,int l,int r,int tl,int tr){
	if (tl > r || tr < l) return ;
	if (tl <= l && tr >= r){
		an += tree[o].sum;
		return ;
	}
	if (l == r) return ;
	pushdown(o,l,r);
	int mid = (l+r)>>1;
	if (tl <= mid)  q_sum(o<<1,l,mid,tl,tr);
	if (tr > mid)  q_sum(o<<1|1,mid+1,r,tl,tr);
}
int Bs(int tl,int F){
	an = 0;
	q_sum(1,1,n,tl,n);
	if (an == 0) return -1;
	if (an < F) return n; 
	int l = tl,r = n;
	int ans = tl;
	while (l <= r){
		int mid = (l+r)>>1;
		an = 0;
		q_sum(1,1,n,tl,mid);
		if (an >= F) {
			r = mid - 1;
			ans = mid;
		}else l = mid + 1;
	}
	return ans;
}
int ans1,ans2;
void query1(int o,int l,int r,int tl,int tr){
	if(tl <= l && tr >= r){
		if (tree[o].first != -1)
		ans1 = tree[o].first;
		return ;
	}
	if (l == r) return ;
	pushdown(o,l,r);
	int mid = (l+r)>>1;
	if (tl <= mid)   query1(o<<1,l,mid,tl,tr); 
	if(ans1!=-1) return ;
	if (tr > mid)  query1(o<<1|1,mid+1,r,tl,tr);
}
void query2(int o,int l,int r,int tl,int tr){
	if (tl > r || tr < l) return ;
	if(tl <= l && tr >= r){
		if (tree[o].last!=-1)
		ans2 = tree[o].last;
		return;
	}
	if (l == r) return ;
	pushdown(o,l,r);
	int mid = (l+r)>>1;
	if (tl <= mid)  query2(o<<1,l,mid,tl,tr); 
	if (tr > mid) query2(o<<1|1,mid+1,r,tl,tr);
}
int main() {
	int t;
	scanf("%d",&t);
	while (t--){
		scanf("%d%d",&n,&m);
		int k,a,b;
		build(1,1,n);
		while (m--){
			scanf("%d%d%d",&k,&a,&b);
			if (k == 1){
			    a++;
				int tr = Bs(a,b);
				if (tr == -1){
					puts("Can not put any one.");
					continue;
				} 
				ans1 = -1;ans2 = -1;
				query1(1,1,n,a,tr);
				query2(1,1,n,a,tr);	
				printf("%d %d\n",ans1-1,ans2-1);
				update(1,1,n,a,tr,0);
				
			}else{
				an = 0;
				a++,b++;
				q_sum(1,1,n,a,b);
				printf("%d\n",b-a+1-an);
				update(1,1,n,a,b,1);
				
			}
		}
		printf("\n");
	}
	return 0;
}

你可能感兴趣的:(每周(→∞)记录?)