线段树

线段树

 基本的线段树就略过去了,基本线段树不会写这一年也白学了。

 区间修改的时候要懒惰标记。如果有加和乘的话要考虑哪一个操作先。然后如果是乘的话一开始要初始化为 \(1\)

 动态开点线段树:只有当访问到某个子节点的时候才开这个节点。

 标记永久化:如果确定懒惰标记不会爆的话,可以不下传懒惰标记,而是在需要求的时候把那个值带在询问里面往下传。但是这个必须要求区间贡献独立。

 还做了一道树链剖分的题目,那道题放到树链剖分里面去了。link,理论上来说树链剖分里面每一道题目都用了线段树。

T1 2824 排序

 二分答案,将小于 \(mid\) 的值全部赋值为 \(1\) ,大于 \(mid\) 的值全部赋值为 \(0\),然后区间查询一下这里有多少个 \(1\)(或者反过来),这也就是当前的 \(mid\) 在哪一个位置,当这个位置是 \(q\) 的时候,那么就说明 \(mid\) 就是查询的值。(应该吧,郭总说这是整体二分模板题,找个时间把整体二分给学了)。

#define ir idx * 2 + 1
#define il idx * 2
#define L tr[idx].l
#define R tr[idx].r

struct node{
	int l,r;
	int sum,lazy;
}tr[N << 2];

struct wen{
	lwl type;
	lwl l,r;
}q[N];

int n,m,k,mid;
int w[N];

void push_up(int idx) {
	tr[idx].sum = (tr[il].sum + tr[ir].sum);
}

void push_down(int idx) {
	int t = tr[idx].lazy;
	if (t == -1) return ;
	int mid = (L + R) >> 1;
	tr[il].sum = (mid - L + 1) * t;
	tr[ir].sum = (R - mid) * t;
	tr[il].lazy = tr[ir].lazy = t;
	tr[idx].lazy = -1;
}

void build(int l,int r,int idx) {
	if (l > r) return ;
	L = l,R = r;
	tr[idx].lazy = -1;
	if (l == r) {
		tr[idx].sum = (w[l] >= mid);
		return ;
	}
	int mid = (L + R) >> 1;
	if (mid >= l) build(l,mid,il);
	if (mid < r) build(mid + 1,r,ir);
	push_up(idx);
}

void update(int l,int r,int idx,int val) {
	if (l > r) return ;
	if (L >= l && R <= r) {
		tr[idx].sum = (R - L + 1) * val;
		tr[idx].lazy = val;
		return ;
	}
	push_down(idx);
	int mid = (L + R) >> 1;
	if (mid >= l) update(l,r,il,val);
	if (mid < r) update(l,r,ir,val);
	push_up(idx);
}

int query(int l,int r,int idx) {
	if (l > r) return 0;
	if (L >= l && R <= r) {
		return tr[idx].sum;
	}
	push_down(idx);
	int ans = 0;
	int mid = (L + R) >> 1;
	if (mid >= l) ans += query(l,r,il);
	if (mid < r) ans += query(l,r,ir);
	return ans;
}

bool check() {
	build(1,n,1);
	for (int i = 1; i <= m; i ++) {
		int type = q[i].type;
		int l = q[i].l,r = q[i].r;
		int cnt = query(l,r,1);
		if (!type) {
			update(r - cnt + 1,r,1,1);
			update(l,r - cnt,1,0);
		} else {
			update(l,l + cnt - 1,1,1);
			update(l + cnt,r,1,0);
		}
	}
	return query(k,k,1);
}

int main(){
	n = fr(),m = fr();
	for (int i = 1; i <= n; i ++) w[i] = fr();
	for (int i = 1; i <= m; i ++) {
		q[i] = {fr(),fr(),fr()};
	}
	int l = 1,r = n;
	int ans = 0;
	k = fr();
	while (l <= r) {
		mid = (l + r) >> 1;
		if (check()) ans = mid,l = mid + 1;
		else r = mid - 1;
	}
	fw(ans);
	return 0;
}

T2 3979 遥远的国度

 如果不换根就是裸的树链剖分,于是单独考虑换根。

 可以发现,换根其实只是对于子树的查询有影响,所以我们查询的时候我们就改一下查询方法,因为可以发现,如果将下图的 \(1\) 点改为根节点的话,那么 \(u\) 的子树就变成了框起来的那部分。可以发现,其实这个地方就是所有的点减去 \(u\) 所对应的二子节点的子树,那么我们就可以查询两个分开的区间,然后再在这两个区间里面取一个最小值。

pCLRRl6.png

T3 Legacy

 线段树优化建图,建两颗线段树,第一棵线段树的边都是从上往下的,第二棵线段树的边都是从下往上的,然后边权都是 \(0\) (所以不能把他们建在一棵树上,要不然所有最短路都是 \(0\) 了)。建出来大概就是下面这个样子:

pCLoh8J.png

 而对于每一个 \(2\) 操作,就从 \(tr2\) 的叶子节点向 \(tr1\) 的对应区间连边(也就是下面绿色的边),而对于 \(3\) 操作来说,我们就从 \(tr2\) 的根节点向 \(tr1\) 的叶子结点连边。

 除此之外,还需要连的边是叶子结点和叶子结点之间的边,因为两棵不同的树的同一个叶子节点对应图中的点是一样的,所以说同样的叶子节点之间要连权值为 \(0\) 的边。(也就是下面的红边)

pCLo6bV.png

 最后跑一遍最短路就可以了。

 注意这一题要开 \(lwl\) ,不仅是 \(dis\) 数组,还有 \(dij\) 里面那个优先队列也要开 \(lwl\)

struct node{
	int l,r;
}tr1[N << 2],tr2[N << 2];

struct Node{
	int v,w;
};

int n,m,st,tot;
vector<Node> e[N << 3];
int leaf1[N],leaf2[N];
lwl dis[N << 4];
bool flag[N << 4];

void add(int a,int b,int val) {
	e[a].push_back({b,val});
}

// up to down
void build_1(int l,int r,int idx) {
	int &L = tr1[idx].l,&R = tr1[idx].r;
	if (l > r) return ;
	L = l,R = r;
	if (l == r) {
		leaf1[l] = idx;
		return ;
	}
	int mid = (L + R) >> 1;
	build_1(l,mid,il);
	build_1(mid + 1,r,ir);
	add(idx,il,0),add(idx,ir,0);
}

// down to up
void build_2(int l,int r,int idx) {
	int &L = tr2[idx].l,&R = tr2[idx].r;
	if (l > r) return ;
	L = l,R = r;
	if (l == r) {
		leaf2[l] = idx + tot;
		add(leaf1[l],leaf2[l],0);
		add(leaf2[l],leaf1[l],0);
		return ;
	}
	int mid = (L + R) >> 1;
	build_2(l,mid,il);
	build_2(mid + 1,r,ir);
	add(il + tot,idx + tot,0),add(ir + tot,idx + tot,0);
}

void add_1(int l,int r,int idx,int from,int val) {
	int &L = tr1[idx].l,&R = tr1[idx].r;
	if (L >= l && R <= r) {
		add(from,idx,val);
		return ;
	}
	int mid = (L + R) >> 1;
	if (mid >= l) add_1(l,r,il,from,val);
	if (mid < r) add_1(l,r,ir,from,val);
}

void add_2(int l,int r,int idx,int to,int val) {
	int &L = tr2[idx].l,&R = tr2[idx].r;
	if (L >= l && R <= r) {
		add(idx + tot,to,val);
		return ;
	}
	int mid = (L + R) >> 1;
	if (mid >= l) add_2(l,r,il,to,val);
	if (mid < r) add_2(l,r,ir,to,val);
}

void dij() {
	memset(dis,0x3f,sizeof dis);
	priority_queue<pii,vector<pii>,greater<pii> > q;
	dis[leaf2[st]] = 0;
	q.push({dis[leaf2[st]],leaf2[st]});
	
	while (q.size()) {
		auto u = q.top().se;
		q.pop();
		
		if (flag[u]) continue;
		
		for (auto it : e[u]) {
			int v = it.v,w = it.w;
			if (dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				q.push({dis[v],v});
			}
		}
		
		flag[u] = true;
	}
}

int main(){
	n = fr(),m = fr(),st = fr();
	build_1(1,n,1);
	tot = n << 2;
	build_2(1,n,1);
	while (m --) {
		int type = fr();
		if (type == 1) {
			int u = fr(),v = fr(),w = fr();
			add(leaf2[u],leaf1[v],w);
		} else if (type == 2) {
			int u = fr(),l = fr(),r = fr(),w = fr();
			add_1(l,r,1,leaf2[u],w);
		} else {
			int v = fr(),l = fr(),r = fr(),w = fr();
			add_2(l,r,1,leaf1[v],w);
		}
	}
	dij();
	for (int i = 1; i <= n; i ++) {
		if (dis[leaf1[i]] > linf / 2) fw(-1);
		else fw(dis[leaf1[i]]);
		kg;
	}
	return 0;
}

练习

 今天的题做且仅会做第一题。你妈的。鉴定为 \(B,C\) 题都是不可做题。等老师的代码吧。

A.快递小哥

 线段树优化建树,这个是区间和区间之间连边,和前面那个差不多。然后一开始有好多人因为没有输出 \(-1\) \(WA\) 了,但是我写了,乐。

 区间连区间的话和前面那个差不多,但是多点连多点有点麻烦,所以为每一组边减一个虚点,最后距离除以 \(2\) 也可以(潇潇写的是连两个点然后这两个点之间的边有权值,然后两个区间连向这两个点,边权为 \(0\)

 一开始这道题 \(dij\) 写错了,恼。

struct node{
	int l,r;
};

struct Node{
	int v,w;
};

int n,m,st;
int tot = 0;
node tr1[N << 2],tr2[N << 2];
vector<Node> e[N << 4];
int leaf1[N],leaf2[N];
int dis[N << 4],flag[N << 4];

void add(int a,int b,int val) {
	e[a].push_back({b,val});
}

// from up -> down
void build_1(int l,int r,int idx) {
	int &L = tr1[idx].l,&R = tr1[idx].r;
	if (l > r) return ;
	L = l,R = r;
	if (l == r) {
		leaf1[l] = idx;
		return ;
	}
	int mid = (l + r) >> 1;
	build_1(l,mid,il);
	build_1(mid + 1,r,ir);
	add(idx,il,0),add(idx,ir,0);
}

// from down -> up
void build_2(int l,int r,int idx) {
	int &L = tr2[idx].l,&R = tr2[idx].r;
	if (l > r) return ;
	L = l,R = r;
	if (l == r) {
		leaf2[l] = idx + tot;
		return ;
	}
	int mid = (l + r) >> 1;
	build_2(l,mid,il);
	build_2(mid + 1,r,ir);
	add(il + tot,idx + tot,0),add(ir + tot,idx + tot,0);
}

void add_1(int l,int r,int idx,int to) {
	int &L = tr1[idx].l,&R = tr1[idx].r;
	if (L >= l && R <= r) {
		add(to,idx,1);
		return ;
	}
	int mid = (L + R) >> 1;
	if (mid >= l) add_1(l,r,il,to);
	if (mid < r) add_1(l,r,ir,to);
}

void add_2(int l,int r,int idx,int to) {
	int &L = tr2[idx].l,&R = tr2[idx].r;
	if (L >= l && R <= r) {
		add(idx + tot,to,1);
		return ;
	}
	int mid = (L + R) >> 1;
	if (mid >= l) add_2(l,r,il,to);
	if (mid < r) add_2(l,r,ir,to);
}

void dij() {
	memset(flag,0,sizeof flag);
	memset(dis,0x3f,sizeof dis);
	priority_queue<pii,vector<pii>,greater<pii> > q;
	dis[leaf2[st]] = 0;
	q.push({dis[leaf2[st]],leaf2[st]});
	
	while (q.size()) {
		auto u = q.top().se;
		q.pop();
		
		if (flag[u]) continue;
		
		for (auto it : e[u]) {
			int v = it.v,w = it.w;
			if (dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				q.push({dis[v],v});
			}
		}
		
		flag[u] = true;
	}
}

int main(){
	n = fr(),m = fr(),st = fr();
	build_1(1,n,1);
	tot = n << 2;
	build_2(1,n,1);
	int k = n << 3;
	for (int i = 1; i <= n; i ++) {
		add(leaf1[i],leaf2[i],0);
		add(leaf2[i],leaf1[i],0);
	}
	while (m --) {
		int a = fr(),b = fr(),c = fr(),d = fr();
		k ++;
		add_1(a,b,1,k);
		add_2(c,d,1,k);
		
		k ++;
		add_1(c,d,1,k);
		add_2(a,b,1,k);
	}
	dij();
	for (int i = 1; i <= n; i ++) {
		if (dis[leaf1[i]] >= inf) wj;
		else {
			fw(dis[leaf1[i]] / 2);
			ch;
		}
	}
	return 0;
}

B.菜鸟驿站

 对着代码意会一下吧()

#define L tr[idx].l
#define R tr[idx].r
#define ir idx * 2 + 1
#define il idx * 2

struct node{
	int l,r;
	int maxn,lazy;
}tr[N << 2];

int n[2];
char s[2][N];
int w[2][N],qwq[4][N << 2];
int tot[N],nw[N],lis[N << 2];

void push_up(int idx) {
	tr[idx].maxn = max(tr[il].maxn,tr[ir].maxn);
}

void push_down(int idx) {
	if (!tr[idx].lazy) return ;
	int t = tr[idx].lazy;
	tr[idx].lazy = 0;
	tr[il].maxn += t,tr[ir].maxn += t;
	tr[il].lazy += t,tr[ir].lazy += t;
}

void build(int l,int r,int idx) {
	if (l > r) return ;
	L = l,R = r;
	tr[idx].lazy = 0;
	if (l == r) {
		tr[idx].maxn = lis[l];
		return ;
	}
	int mid = (L + R) >> 1;
	build(l,mid,il);
	build(mid + 1,r,ir);
	push_up(idx);
}

void update(int l,int r,int idx,int val) {
	if (l > r) return ;
	if (L >= l && R <= r) {
		tr[idx].lazy += val;
		tr[idx].maxn += val;
		return ;
	}
	push_down(idx);
	int mid = (L + R) >> 1;
	if (mid >= l) update(l,r,il,val);
	if (mid < r) update(l,r,ir,val);
	push_up(idx);
}

int main(){
	int T = fr();
	while (T --) {
		scanf("%s%s",s[0] + 1,s[1] + 1);
		n[0] = strlen(s[0] + 1),n[1] = strlen(s[1] + 1);
		memset(tot,0,sizeof tot);
		memset(nw,0,sizeof nw);
		for (int i = 0; i < 2; i ++) {
			for (int j = 1; j <= n[i]; j ++) {
				if (s[i][j] == 'A') w[i][j] = 1;
				else if (s[i][j] == 'C') w[i][j] = 2;
				else if (s[i][j] == 'G') w[i][j] = 3;
				else w[i][j] = 0;
				tot[w[i][j]] ++;
			}
		}
		for (int i = 0; i < 4; i ++) {
			for (int j = 1; j <= n[0] + n[1]; j ++) {
				qwq[i][j] = inf;
			}
		}
		memset(lis,0,sizeof lis);
		for (int i = 1; i <= n[1] + 1; i ++) {
			if (i != n[1] + 1) qwq[w[1][i]][++ nw[w[1][i]]] = i;
			for (int j = 0; j < 4; j ++)
				lis[i] += min(nw[j],tot[j] - nw[j]);
		}
		build(0,n[1],1);
		memset(nw,0,sizeof nw);
		int ans = tr[1].maxn;
		for (int i = 1; i <= n[0]; i ++) {
			int t = w[0][i];
			++ nw[t];
			if (nw[t] > (tot[t] + 1) / 2) {
				update(0,n[1],1,-1);
			} else {
				update(0,min(qwq[t][tot[t] / 2 - nw[t] + 1],n[1]) - 1,1,1);
				update(qwq[t][(tot[t] + 1) / 2 - nw[t] + 1],n[1],1,-1);
			}
			ans = max(ans,tr[1].maxn);
		}
		fw(ans);
		ch;
	}
	return 0;
}
posted @ 2023-07-24 18:39  jingyu0929  阅读(10)  评论(0)    收藏  举报