洛谷-【数据结构2-2】线段树2

P1253 扶苏的问题

题目描述

给定一个长度为 n 的序列 a,要求支持如下三个操作:

  1. 给定区间 [l,r],将区间内每个数都修改为 x。
  2. 给定区间 [l,r],将区间内每个数都加上 x。
  3. 给定区间 [l,r],求区间内的最大值。

输入格式

第一行是两个整数,依次表示序列的长度 n 和操作的个数 q。

第二行有 n 个整数,第 i 个整数表示序列中的第 i 个数 ai​。

接下来 q 行,每行表示一个操作。每行首先有一个整数 op,表示操作的类型。

  • 若 op=1,则接下来有三个整数 l,r,x,表示将区间 [l,r] 内的每个数都修改为 x。
  • 若 op=2,则接下来有三个整数 l,r,x,表示将区间 [l,r] 内的每个数都加上 x。
  • 若 op=3,则接下来有两个整数 l,r,表示查询区间 [l,r] 内的最大值。

输出格式

对于每个 op=3 的操作,输出一行一个整数表示答案。

输入输出样例

输入 #1复制

复制代码
6 6
1 1 4 5 1 4
1 1 2 6
2 3 4 2
3 1 4
3 2 3
1 1 6 -1
3 1 6

输出 #1复制

复制代码
7
6
-1

输入 #2复制

复制代码
4 4
10 4 -3 -7
1 1 3 0
2 3 4 -4
1 2 4 -9
3 1 4

输出 #2复制

复制代码
0

说明/提示

数据规模与约定

  • 对于 10% 的数据,n=q=1。
  • 对于 40% 的数据,n,q≤103。
  • 对于 50% 的数据,0≤ai,x≤104。
  • 对于 60% 的数据,op=1。
  • 对于 90% 的数据,n,q≤105。
  • 对于 100% 的数据,1≤n,q≤106,1≤l,r≤n,op∈{1,2,3},∣ai∣,∣x∣≤109。

提示

请注意大量数据读入对程序效率造成的影响。

实现代码:

cpp 复制代码
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <iostream>

typedef long long int ll;

const int maxn = 1000006;

ll nul = 1e18;

int n, q;
int a[maxn];

struct Node {
  int l, r;
  ll w, t1, t2;
  Node *ls, *rs;

  void make_tag1(ll x) {
    w = t1 = x;
    t2 = 0;
  }

  void make_tag2(ll x) {
    w += x;
    if (t1 != nul)
      t1 += x;
    else
      t2 += x;
  }

  void pushdown() {
    if (t1 != nul) {
      ls->make_tag1(t1);
      rs->make_tag1(t1);
      t1 = nul;
    } else if (t2) {
      ls->make_tag2(t2);
      rs->make_tag2(t2);
      t2 = 0;
    }
  }

  void pushup() { w = std::max(ls->w, rs->w); }

  bool InRange(int L, int R) { return (L <= l) && (r <= R); }
  bool OutofRange(int L, int R) { return (l > R) || (r < L); }

  void upd(int L, int R, int x, int op) {
    if (InRange(L, R)) {
      if (op == 1)
        make_tag1(x);
      else
        make_tag2(x);
    } else if (!OutofRange(L, R)) {
      pushdown();
      ls->upd(L, R, x, op);
      rs->upd(L, R, x, op);
      pushup();
    }
  }

  ll qry(int L, int R) {
    if (InRange(L, R))
      return w;
    else if (!OutofRange(L, R)) {
      pushdown();
      return std::max(ls->qry(L, R), rs->qry(L, R));
    } else
      return -nul;
  }
};

Node Mem[maxn << 1], *pool = Mem;

Node* New(int L, int R) {
  auto u = pool++;
  u->l = L;
  u->r = R;
  u->t1 = nul;
  u->t2 = 0;
  if (L != R) {
    int M = (L + R) >> 1;
    u->ls = New(L, M);
    u->rs = New(M + 1, R);
    u->pushup();
  } else {
    u->w = a[L];
  }
  return u;
}

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);
  std::cout.tie(0);
  std::cin >> n >> q;
  for (int i = 1; i <= n; ++i) {
    std::cin >> a[i];
  }
  auto rot = New(1, n);
  for (int op, l, r, x; q; --q) {
    std::cin >> op >> l >> r;
    if (op != 3) {
      std::cin >> x;
      rot->upd(l, r, x, op);
    } else {
      std::cout << rot->qry(l, r) << '\n';
    }
  }
  return 0;
}

P3373 【模板】线段树 2

题目描述

如题,已知一个数列 a,你需要进行下面三种操作:

  • 将某区间每一个数乘上 x;
  • 将某区间每一个数加上 x;
  • 求出某区间每一个数的和。

输入格式

第一行包含三个整数 n,q,m,分别表示该数列数字的个数、操作的总个数和模数。

第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值 ai​。

接下来 q 行每行包含若干个整数,表示一个操作,具体如下:

操作 1: 格式:1 x y k 含义:将区间 [x,y] 内每个数乘上 k。

操作 2: 格式:2 x y k 含义:将区间 [x,y] 内每个数加上 k。

操作 3: 格式:3 x y 含义:输出区间 [x,y] 内每个数的和对 m 取模所得的结果。

输出格式

输出包含若干行整数,即为所有操作 3 的结果。

输入输出样例

输入 #1复制

复制代码
5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4

输出 #1复制

复制代码
17
2

说明/提示

【数据范围】

对于 30% 的数据:n≤8,q≤10。

对于 70% 的数据:n≤103,q≤104。

对于 100% 的数据:1≤n≤105,1≤q≤105,1≤ai​,k≤104。

除样例外,m=571373。

(数据已经过加强 ^_^)

样例说明:

故输出应为 17、2(40mod38=2)。

实现代码:

cpp 复制代码
#include <bits/stdc++.h>

#define MAXN 100010
#define ll long long

using namespace std;

int n, m, mod;
int a[MAXN];

struct Segment_Tree {
	ll sum, add, mul;
	int l, r;
}s[MAXN * 4];

void update(int pos) {
	s[pos].sum = (s[pos << 1].sum + s[pos << 1 | 1].sum) % mod;
    return;
}

void pushdown(int pos) {
	s[pos << 1].sum = (s[pos << 1].sum * s[pos].mul + s[pos].add * (s[pos << 1].r - s[pos << 1].l + 1)) % mod;
	s[pos << 1 | 1].sum = (s[pos << 1 | 1].sum * s[pos].mul + s[pos].add * (s[pos << 1 | 1].r - s[pos << 1 | 1].l + 1)) % mod;
	
	s[pos << 1].mul = (s[pos << 1].mul * s[pos].mul) % mod;
	s[pos << 1 | 1].mul = (s[pos << 1 | 1].mul * s[pos].mul) % mod;
	
	s[pos << 1].add = (s[pos << 1].add * s[pos].mul + s[pos].add) % mod;
	s[pos << 1 | 1].add = (s[pos << 1 | 1].add * s[pos].mul + s[pos].add) % mod;
		
	s[pos].add = 0;
	s[pos].mul = 1;
	return; 
}

void build_tree(int pos, int l, int r) {
	s[pos].l = l;
	s[pos].r = r;
	s[pos].mul = 1;
	
	if (l == r) {
		s[pos].sum = a[l] % mod;
		return;
	}
	
	int mid = (l + r) >> 1;
	build_tree(pos << 1, l, mid);
	build_tree(pos << 1 | 1, mid + 1, r);
	update(pos);
	return;
}

void ChangeMul(int pos, int x, int y, int k) {
	if (x <= s[pos].l && s[pos].r <= y) {
		s[pos].add = (s[pos].add * k) % mod;
		s[pos].mul = (s[pos].mul * k) % mod;
		s[pos].sum = (s[pos].sum * k) % mod;
		return;
	}
	
	pushdown(pos);
	int mid = (s[pos].l + s[pos].r) >> 1;
	if (x <= mid) ChangeMul(pos << 1, x, y, k);
	if (y > mid) ChangeMul(pos << 1 | 1, x, y, k);
	update(pos);
	return;
}

void ChangeAdd(int pos, int x, int y, int k) {
	if (x <= s[pos].l && s[pos].r <= y) {
		s[pos].add = (s[pos].add + k) % mod;
		s[pos].sum = (s[pos].sum + k * (s[pos].r - s[pos].l + 1)) % mod;
		return;
	}
	
	pushdown(pos);
	int mid = (s[pos].l + s[pos].r) >> 1;
	if (x <= mid) ChangeAdd(pos << 1, x, y, k);
	if (y > mid) ChangeAdd(pos << 1 | 1, x, y, k);
	update(pos);
	return;
}

ll AskRange(int pos, int x, int y) {
	if (x <= s[pos].l && s[pos].r <= y) {
		return s[pos].sum;
	}
	
	pushdown(pos);
	ll val = 0;
	int mid = (s[pos].l + s[pos].r) >> 1;
	if (x <= mid) val = (val + AskRange(pos << 1, x, y)) % mod;
	if (y > mid) val = (val + AskRange(pos << 1 | 1, x, y)) % mod;
	return val;
}

int main() {
	scanf("%d%d%d", &n, &m, &mod);
	
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
	}
	
	build_tree(1, 1, n);
	
	for (int i = 1; i <= m; i++) {
		int opt, x, y;
		scanf("%d%d%d", &opt, &x, &y);
		if (opt == 1) {
			int k;
			scanf("%d", &k);
			ChangeMul(1, x, y, k);
		}
		if (opt == 2) {
			int k;
			scanf("%d", &k);
			ChangeAdd(1, x, y, k);
		}
		if (opt == 3) {
			printf("%lld\n", AskRange(1, x, y));
		}
	}
    
	return 0;
}

P4513 小白逛公园

题目背景

小新经常陪小白去公园玩,也就是所谓的遛狗啦...

题目描述

在小新家附近有一条"公园路",路的一边从南到北依次排着 n 个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。

一开始,小白就根据公园的风景给每个公园打了分。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第 a 个和第 b 个公园之间(包括 a,b 两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。

那么,就请你来帮小白选择公园吧。

输入格式

第一行,两个整数 n 和 m,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。

接下来 n 行,每行一个整数,依次给出小白开始时对公园的打分。

接下来 m 行,每行三个整数。其中第一个整数 k 为 1 或 2。

  • k=1 表示,小新要带小白出去玩,接下来的两个整数 a 和 b 给出了选择公园的范围 (1≤a,b≤n)。测试数据可能会出现 a>b 的情况,需要进行交换;
  • k=2 表示,小白改变了对某个公园的打分,接下来的两个整数 p 和 s,表示小白对第 p 个公园的打分变成了 s(1≤∣s∣≤1000)。

输出格式

小白每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分和的最大值。

输入输出样例

输入 #1复制

复制代码
5 3
1
2
-3
4
5
1 2 3
2 2 -1
1 2 3

输出 #1复制

复制代码
2
-1

说明/提示

数据规模与约定

对于 100% 的数据,1≤n≤5×105,1≤m≤105,所有打分都是绝对值不超过 1000 的整数。

实现代码:

cpp 复制代码
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 500001
struct Node{int maxv,maxl,maxr,sumv;}T[N<<2];
inline void pushup(Node &rt,const Node &ls,const Node &rs)
{
	if(ls.maxr<0 && rs.maxl<0)
	  rt.maxv=max(ls.maxr,rs.maxl);
	else
	  {
	  	rt.maxv=0;
	  	if(ls.maxr>0)
	  	  rt.maxv+=ls.maxr;
	  	if(rs.maxl>0)
	  	  rt.maxv+=rs.maxl;
	  }
	rt.maxv=max(rt.maxv,ls.maxv);
	rt.maxv=max(rt.maxv,rs.maxv);
	rt.maxl=max(ls.maxl,ls.sumv+rs.maxl);
	rt.maxr=max(rs.maxr,rs.sumv+ls.maxr);
	rt.sumv=ls.sumv+rs.sumv;
}
void buildtree(int rt,int l,int r)
{
	if(l==r)
	  {
	  	scanf("%d",&T[rt].maxv);
	  	T[rt].sumv=T[rt].maxl=T[rt].maxr=T[rt].maxv;
	  	return;
	  }
	int m=(l+r>>1);
	buildtree(rt<<1,l,m);
	buildtree(rt<<1|1,m+1,r);
	pushup(T[rt],T[rt<<1],T[rt<<1|1]);
}
void update(int p,int v,int rt,int l,int r)
{
	if(l==r)
	  {
	  	T[rt].sumv=T[rt].maxl=T[rt].maxr=T[rt].maxv=v;
	  	return;
	  }
	int m=(l+r>>1);
	if(p<=m) update(p,v,rt<<1,l,m);
	else update(p,v,rt<<1|1,m+1,r);
	pushup(T[rt],T[rt<<1],T[rt<<1|1]);
}
Node query(int ql,int qr,int rt,int l,int r)
{
	if(ql<=l&&r<=qr) return T[rt];
	int m=(l+r>>1);
	if(ql<=m && m<qr)
	  {
	  	Node res;
	  	pushup(res,query(ql,qr,rt<<1,l,m),query(ql,qr,rt<<1|1,m+1,r));
	  	return res;
	  }
	else if(ql<=m) return query(ql,qr,rt<<1,l,m);
	else return query(ql,qr,rt<<1|1,m+1,r);
}
int n,m;
int main()
{
	int op,x,y;
	scanf("%d%d",&n,&m);
	buildtree(1,1,n);
	for(;m;--m)
	  {
	  	scanf("%d%d%d",&op,&x,&y);
	  	if(op==1)
		  {
		  	if(x>y)
		  	  swap(x,y);
		  	printf("%d\n",query(x,y,1,1,n).maxv);
		  }
	  	else update(x,y,1,1,n);
	  }
	return 0;
}

P1908 逆序对

题目描述

猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。

最近,TOM 老猫查阅到一个人类称之为"逆序对"的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 ai​>aj​ 且 i<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。

Update:数据已加强。

输入格式

第一行,一个数 n,表示序列中有 n 个数。

第二行 n 个数,表示给定的序列。序列中每个数字不超过 109。

输出格式

输出序列中逆序对的数目。

输入输出样例

输入 #1复制

复制代码
6
5 4 2 6 3 1

输出 #1复制

复制代码
11

说明/提示

对于 25% 的数据,n≤2500。

对于 50% 的数据,n≤4×104。

对于所有数据,1≤n≤5×105。

应该不会有人 O(n2) 过 50 万吧 ------ 2018.8 chen_zhe。

实现代码:

cpp 复制代码
#include<cstdio>
#include<iostream>
using namespace std;
int n,a[500010],c[500010];
long long ans;

void msort(int b,int e)
{
    if(b==e)  
		return;
    int mid=(b+e)/2,i=b,j=mid+1,k=b;
    msort(b,mid),msort(mid+1,e);
    while(i<=mid&&j<=e)
    	if(a[i]<=a[j])
    		c[k++]=a[i++];
    	else
    		c[k++]=a[j++],ans+=mid-i+1;
    while(i<=mid)
    	c[k++]=a[i++];
    while(j<=e)
    	c[k++]=a[j++];
    for(int l=b;l<=e;l++)
    	a[l]=c[l];
} 

int main()
{
    scanf("%d",&n); 
    for(int i=1;i<=n;i++)
    	scanf("%d",&a[i]);
    msort(1,n);
    printf("%lld",ans);
    return 0;
}
相关推荐
ghie90901 小时前
MATLAB 随机蛙跳算法 (SFLA) 优化最小二乘回归
算法·matlab·回归
wuweijianlove1 小时前
算法优化中的缓存层次结构与内存映射的技术7
算法
故事和你911 小时前
洛谷-【数据结构2-2】线段树1
开发语言·javascript·数据结构·算法·动态规划·图论
鸠摩智首席音效师1 小时前
如何在 Bash 中通过 Amazon SES 发送电子邮件 ?
开发语言·bash
电科一班林耿超1 小时前
机器学习大师课 第 8 课:端到端项目实战 —— 泰坦尼克号生存预测
人工智能·算法·机器学习
~|Bernard|1 小时前
五,go语言的内存管理
开发语言·后端·golang
ComputerInBook1 小时前
数字图像处理(4版)——第 12 章——图像模式分类(上)(Rafael C.Gonzalez&Richard E. Woods)
图像处理·人工智能·算法·模式识别·图像模式分类
y = xⁿ1 小时前
20天速通LeetCodeday13:DFS深度优先搜素
算法·深度优先
七牛开发者1 小时前
开源项目观察|ds4:本地 Agent 推理,不只是把模型跑起来
人工智能·redis·算法·开源