P1253 扶苏的问题
题目描述
给定一个长度为 n 的序列 a,要求支持如下三个操作:
- 给定区间 [l,r],将区间内每个数都修改为 x。
- 给定区间 [l,r],将区间内每个数都加上 x。
- 给定区间 [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;
}