CSP202012-5 星际旅行 满分详解
题目
题目可以在CSP官网中查看到哟!
结果
注意:小编的代码要在CPP11标准 下提交才能满分通过哟!
算法分析
小编在读这道题题目的时候,感受到了题目的背景很花里胡哨,但是将这花里胡哨的背景撕去,看到本题题目的本质后,就能发现这道题用线段树就可以解决,在本题的题解中,小编使用的就是线段树,同时也进行了Lazy标记来进一步提高效率。
线段树是一个对区间修改、区间查询十分高效的一个算法,本题很显然就是对区间进行修改以及查询,用线段树算法是最好的办法啦。如果读者对最基本的线段树不是很了解,读者可以在CSDN搜索介绍线段树算法的博客或者在哔哩哔哩上面搜索介绍线段树的视频,耐心地看完,就可以对基本的线段树有了进一步地了解啦。
当然啦,线段树也是有优化的,利用Lazy标记可以提升线段树的效率,Lazy标记线段树的大致思想就是:修改区间的时候,在这个区间所有的终止节点上打上一个标记,代表这个点要进行什么样的修改;而等到区间查询的时候,如果访问到某个有标记的点,但又不能使用该点的值的时候,就把标记下传,继续线段树流程即可。在这里小编推荐一个博客可以很直观地了解线段树Lazy标记(这篇博客博主画了一个很直观的树结构来介绍Lazy标记的意义)。
接下来,小编将结合代码详细的介绍小编实现代码的过程。
详细代码讲解
1、输入并根据操作种类调用方法
首先输入n和m,n代表动力装置的数量,m代表操作的数量。根据题目,操作一共有四种分别为:动力增加、动力强化、动力转化和动力查询。题目里面说最开始n个动力装置的动力均为0,所以本题的线段树初始都是0,不用利用build来初始化线段树啦。动力增加与动力强化都是对区间进行修改,只是修改的方式不太一样,动力增加主要对区间进行加法,动力强化主要是对区间进行乘法,小编在主函数里面分别调用add()函数以及mul()函数来模拟动力增加与动力强化。动力转化也是属于区间修改的,小编在主函数里面调用rot()函数来模拟动力转化。最后就是动力查询了,这个是属于区间查询范围的,小编在主函数里面调用query()函数来模拟动力查询。具体代码如下:
int main()
{
LL i, j;
//输入n,m
cin >> n >> m;
LL start = 1, end = n, root;
root = ++cnt;
for (i = 1; i <= m; i++)
{
LL type;
cin >> type;
LL l, r;
cin >> l >> r;
//动力增加
if (type == 1)
{
LL a, b, c;
cin >> a >> b >> c;
add(root, start, end, l, r, a, b, c);
}
//动力强化
else if (type == 2)
{
LL k;
cin >> k;
mul(root, start, end, l, r, k);
}
//动力转化
else if (type == 3)
{
rot(root, start, end, l, r);
}
//动力查询
else if (type == 4)
{
LL f = 0, d = 0, e = 0;
query(root, start, end, l, r, f, d, e);
cout << ((f % MOD) * (f % MOD) % MOD + (d % MOD) * (d % MOD) % MOD + (e % MOD) * (e % MOD) % MOD) % MOD << endl;
}
}
return 0;
}
2、交换函数
在实现动力转化期间,会涉及到数值的转化,因此,小编将此过程用一个函数来实现,方便后面的代码直接调用,具体的代码如下:
void swp(LL& a, LL& b)
{
LL temp;
temp = a;
a = b;
b = temp;
}
3、下传操作
带有Lazy标记的线段树在进行区间修改、区间查询的时候,是要进行下传操作的,小编通过调用pushdown()函数来实现这一过程。假设当前线段树操作运行到第p个节点,此时首先先要根据该点的Lazy标记进行相应的更新(一共是5个Lazy标记,分别是x坐标、y坐标、z坐标的增加量的lazy、乘法的Lazy以及转化的Lazy),根据这五个Lazy标记的含义,还是可以很清晰的将其更新到sumx、sumy、sumz数组中的;接下来,将父节点p上面的Lazy标记分别下传到其左右两子节点的Lazy上,同时将p节点自身的Lazy标记清空,具体的代码如下:
void pushdown(LL p, LL start, LL end)
{
if (start == end)
{
lazyx[p] = lazyy[p] = lazyz[p] = lazymul[p] = lazyro[p] = 0;
return;
}
LL& lchild = lson[p];
LL& rchild = rson[p];
if (!lchild) lchild = ++cnt;
if (!rchild) rchild = ++cnt;
LL mid = (start + end) >> 1;
if (!lazymul[p]) lazymul[p] = 1;
sumx[lchild] = (sumx[lchild] * (lazymul[p] % MOD) % MOD + ((mid - start + 1) % MOD) * (lazyx[p] % MOD) % MOD) % MOD;
sumy[lchild] = (sumy[lchild] * (lazymul[p] % MOD) % MOD + ((mid - start + 1) % MOD) * (lazyy[p] % MOD) % MOD) % MOD;
sumz[lchild] = (sumz[lchild] * (lazymul[p] % MOD) % MOD + ((mid - start + 1) % MOD) * (lazyz[p] % MOD) % MOD) % MOD;
sumx[rchild] = (sumx[rchild] * (lazymul[p] % MOD) % MOD + ((end - mid) % MOD) * (lazyx[p] % MOD) % MOD) % MOD;
sumy[rchild] = (sumy[rchild] * (lazymul[p] % MOD) % MOD + ((end - mid) % MOD) * (lazyy[p] % MOD) % MOD) % MOD;
sumz[rchild] = (sumz[rchild] * (lazymul[p] % MOD) % MOD + ((end - mid) % MOD) * (lazyz[p] % MOD) % MOD) % MOD;
for (LL i = 0; i < lazyro[p]; i++)
{
swp(sumx[lchild], sumy[lchild]);
swp(sumy[lchild], sumz[lchild]);
swp(sumx[rchild], sumy[rchild]);
swp(sumy[rchild], sumz[rchild]);
}
LL u, v, w;
u = lazyx[p];
v = lazyy[p];
w = lazyz[p];
for (LL i = 0; i < lazyro[lchild]; i++)
{
swp(v, w);
swp(u, v);
}
lazyx[lchild] = (lazyx[lchild] * (lazymul[p] % MOD) % MOD + u % MOD) % MOD;
lazyy[lchild] = (lazyy[lchild] * (lazymul[p] % MOD) % MOD + v % MOD) % MOD;
lazyz[lchild] = (lazyz[lchild] * (lazymul[p] % MOD) % MOD + w % MOD) % MOD;
u = lazyx[p];
v = lazyy[p];
w = lazyz[p];
for (LL i = 0; i < lazyro[rchild]; i++)
{
swp(v, w);
swp(u, v);
}
lazyx[rchild] = (lazyx[rchild] * (lazymul[p] % MOD) % MOD + u % MOD) % MOD;
lazyy[rchild] = (lazyy[rchild] * (lazymul[p] % MOD) % MOD + v % MOD) % MOD;
lazyz[rchild] = (lazyz[rchild] * (lazymul[p] % MOD) % MOD + w % MOD) % MOD;
if (lazymul[p] != 1)
{
if (!lazymul[lchild]) lazymul[lchild] = 1;
lazymul[lchild] = (lazymul[lchild] * (lazymul[p] % MOD)) % MOD;
if (!lazymul[rchild]) lazymul[rchild] = 1;
lazymul[rchild] = (lazymul[rchild] * (lazymul[p] % MOD)) % MOD;
}
lazyro[lchild] = (lazyro[lchild] + lazyro[p]) % 3;
lazyro[rchild] = (lazyro[rchild] + lazyro[p]) % 3;
lazyx[p] = 0;
lazyy[p] = 0;
lazyz[p] = 0;
lazymul[p] = 0;
lazyro[p] = 0;
}
4、add()函数模拟动力增加
首先根据start、end、l、r四者直接的关系进行不同的操作(这个是线段树的基本知识):如果当前节点的范围完全不在动力增加的范围,则直接返回即可;如果当前如果当前节点的范围完全在动力增加的范围,则直接对该节点的sumx、sumy、sumz数组以及lazy数组进行更新;其余的情况就是当前节点与动力增加的范围有交集,此时如果该节点有Lazy标记,则要进行下传操作,直接调用pushdown()函数即可。之后将当前节点进行二分,根据与l,r动力增加范围之间的关系对子节点进行add()操作即可;最后根据左右子节点的结果进行对父节点p的更新即可。具体代码如下:
void add(LL &p, LL start, LL end, LL l, LL r, LL a, LL b, LL c)
{
if (!p) p = ++cnt;
if (end <l || start>r) return;
if (start >= l && end <= r)
{
sumx[p] = (sumx[p] + (end - start + 1) % MOD * (a % MOD) % MOD) % MOD;
sumy[p] = (sumy[p] + (end - start + 1) % MOD * (b % MOD) % MOD) % MOD;
sumz[p] = (sumz[p] + (end - start + 1) % MOD * (c % MOD) % MOD) % MOD;
for (LL i = 0; i < lazyro[p]; i++)
{
swp(b, c);
swp(a, b);
}
lazyx[p] = (lazyx[p] + a % MOD) % MOD;
lazyy[p] = (lazyy[p] + b % MOD) % MOD;
lazyz[p] = (lazyz[p] + c % MOD) % MOD;
return;
}
if (lazyx[p] || lazyy[p] || lazyz[p] || lazymul[p] || lazyro[p]) pushdown(p, start, end);
LL mid = (start + end) >> 1;
if (mid >= l)
add(lson[p], start, mid, l, r, a, b, c);
if (mid < r)
add(rson[p], mid + 1, end, l, r, a, b, c);
sumx[p] = (sumx[lson[p]] % MOD + sumx[rson[p] % MOD]) % MOD;
sumy[p] = (sumy[lson[p]] % MOD + sumy[rson[p] % MOD]) % MOD;
sumz[p] = (sumz[lson[p]] % MOD + sumz[rson[p] % MOD]) % MOD;
}
5、mul()函数模拟动力强化
与上一部分同属区间修改范畴,基本差不多。首先根据start、end、l、r四者直接的关系进行不同的操作(这个是线段树的基本知识):如果当前节点的范围完全不在动力强化的范围,则直接返回即可;如果当前如果当前节点的范围完全在动力强化的范围,则直接对该节点的sumx、sumy、sumz数组以及lazy数组进行更新;其余的情况就是当前节点与动力强化的范围有交集,此时如果该节点有Lazy标记,则要进行下传操作,直接调用pushdown()函数即可。之后将当前节点进行二分,根据与l,r动力强化范围之间的关系对子节点进行mul()操作即可;最后根据左右子节点的结果进行对父节点p的更新即可。具体代码如下:
void mul(LL &p, LL start, LL end, LL l, LL r, LL k)
{
if (!p) p = ++cnt;
if (end <l || start>r) return;
if (start >= l && end <= r)
{
if (!lazymul[p]) lazymul[p] = 1;
sumx[p] = (sumx[p] * (k % MOD)) % MOD;
sumy[p] = (sumy[p] * (k % MOD)) % MOD;
sumz[p] = (sumz[p] * (k % MOD)) % MOD;
lazyx[p] = (lazyx[p] * (k % MOD)) % MOD;
lazyy[p] = (lazyy[p] * (k % MOD)) % MOD;
lazyz[p] = (lazyz[p] * (k % MOD)) % MOD;
lazymul[p] = (lazymul[p] * (k % MOD)) % MOD;
return;
}
if (lazyx[p] || lazyy[p] || lazyz[p] || lazymul[p] || lazyro[p])
pushdown(p, start, end);
LL mid = (start + end) >> 1;
if (mid >= l)
mul(lson[p], start, mid, l, r, k);
if (mid < r)
mul(rson[p], mid + 1, end, l, r, k);
sumx[p] = (sumx[lson[p]] % MOD + sumx[rson[p] % MOD]) % MOD;
sumy[p] = (sumy[lson[p]] % MOD + sumy[rson[p] % MOD]) % MOD;
sumz[p] = (sumz[lson[p]] % MOD + sumz[rson[p] % MOD]) % MOD;
}
6、rot()函数模拟动力转化
与上一部分同属区间修改范畴,基本差不多。首先根据start、end、l、r四者直接的关系进行不同的操作(这个是线段树的基本知识):如果当前节点的范围完全不在动力转化的范围,则直接返回即可;如果当前如果当前节点的范围完全在动力转化的范围,则直接对该节点的sumx、sumy、sumz数组以及lazy数组进行更新;其余的情况就是当前节点与动力转化的范围有交集,此时如果该节点有Lazy标记,则要进行下传操作,直接调用pushdown()函数即可。之后将当前节点进行二分,根据与l,r动力转化范围之间的关系对子节点进行mul()操作即可;最后根据左右子节点的结果进行对父节点p的更新即可。具体代码如下:
void rot(LL &p, LL start, LL end, LL l, LL r)
{
if (!p) p = ++cnt;
if (end<l || start>r) return;
if (start >= l && end <= r)
{
swp(sumx[p], sumy[p]);
swp(sumy[p], sumz[p]);
lazyro[p] ++;
lazyro[p] = lazyro[p] % 3;
return;
}
if (lazyx[p] || lazyy[p] || lazyz[p] || lazymul[p] || lazyro) pushdown(p, start, end);
LL mid = (start + end) >> 1;
if (mid >= l)
rot(lson[p], start, mid, l, r);
if (mid < r)
rot(rson[p], mid + 1, end, l, r);
sumx[p] = (sumx[lson[p]] % MOD + sumx[rson[p] % MOD]) % MOD;
sumy[p] = (sumy[lson[p]] % MOD + sumy[rson[p] % MOD]) % MOD;
sumz[p] = (sumz[lson[p]] % MOD + sumz[rson[p] % MOD]) % MOD;
}
7、query()函数模拟动力查询
这一部分的操作属于区间查询的范畴。首先根据start、end、l、r四者直接的关系进行不同的操作(这个是线段树的基本知识):如果当前节点的范围完全不在动力转化的范围,则直接返回即可;如果当前如果当前节点的范围完全在动力转化的范围,则直接将该节点的sumx、sumy、sumz数值更新到a,b,c中;其余的情况就是当前节点与动力转化的范围有交集,此时如果该节点有Lazy标记,则要进行下传操作,直接调用pushdown()函数即可。之后将当前节点进行二分,根据与l,r动力转化范围之间的关系对子节点进行query()操作即可。具体代码如下:
void query(LL &p, LL start, LL end, LL l, LL r, LL &a, LL &b, LL &c)
{
if (!p) p = ++cnt;
if (end<l || start>r) return;
if (start >= l && end <= r)
{
a = (a + sumx[p] % MOD) % MOD;
b = (b + sumy[p] % MOD) % MOD;
c = (c + sumz[p] % MOD) % MOD;
return;
}
if (lazyx[p] || lazyy[p] || lazyz[p] || lazymul[p] || lazyro[p]) pushdown(p, start, end);
LL mid = (start + end) >> 1;
if (mid >= l)
query(lson[p], start, mid, l, r, a, b, c);
if (mid < r)
query(rson[p], mid + 1, end, l, r, a, b, c);
}
完整满分代码
#include<iostream>
#include<cstdio>
#define MOD 1000000007
#define maxn 10000000
using namespace std;
using LL = long long int;
LL n, m;
LL cnt = 0;
LL sumx[maxn], sumy[maxn], sumz[maxn];
LL lson[maxn], rson[maxn];
LL lazyx[maxn], lazyy[maxn], lazyz[maxn], lazymul[maxn], lazyro[maxn];
void swp(LL& a, LL& b)
{
LL temp;
temp = a;
a = b;
b = temp;
}
void pushdown(LL p, LL start, LL end)
{
if (start == end)
{
lazyx[p] = lazyy[p] = lazyz[p] = lazymul[p] = lazyro[p] = 0;
return;
}
LL& lchild = lson[p];
LL& rchild = rson[p];
if (!lchild) lchild = ++cnt;
if (!rchild) rchild = ++cnt;
LL mid = (start + end) >> 1;
if (!lazymul[p]) lazymul[p] = 1;
sumx[lchild] = (sumx[lchild] * (lazymul[p] % MOD) % MOD + ((mid - start + 1) % MOD) * (lazyx[p] % MOD) % MOD) % MOD;
sumy[lchild] = (sumy[lchild] * (lazymul[p] % MOD) % MOD + ((mid - start + 1) % MOD) * (lazyy[p] % MOD) % MOD) % MOD;
sumz[lchild] = (sumz[lchild] * (lazymul[p] % MOD) % MOD + ((mid - start + 1) % MOD) * (lazyz[p] % MOD) % MOD) % MOD;
sumx[rchild] = (sumx[rchild] * (lazymul[p] % MOD) % MOD + ((end - mid) % MOD) * (lazyx[p] % MOD) % MOD) % MOD;
sumy[rchild] = (sumy[rchild] * (lazymul[p] % MOD) % MOD + ((end - mid) % MOD) * (lazyy[p] % MOD) % MOD) % MOD;
sumz[rchild] = (sumz[rchild] * (lazymul[p] % MOD) % MOD + ((end - mid) % MOD) * (lazyz[p] % MOD) % MOD) % MOD;
for (LL i = 0; i < lazyro[p]; i++)
{
swp(sumx[lchild], sumy[lchild]);
swp(sumy[lchild], sumz[lchild]);
swp(sumx[rchild], sumy[rchild]);
swp(sumy[rchild], sumz[rchild]);
}
LL u, v, w;
u = lazyx[p];
v = lazyy[p];
w = lazyz[p];
for (LL i = 0; i < lazyro[lchild]; i++)
{
swp(v, w);
swp(u, v);
}
lazyx[lchild] = (lazyx[lchild] * (lazymul[p] % MOD) % MOD + u % MOD) % MOD;
lazyy[lchild] = (lazyy[lchild] * (lazymul[p] % MOD) % MOD + v % MOD) % MOD;
lazyz[lchild] = (lazyz[lchild] * (lazymul[p] % MOD) % MOD + w % MOD) % MOD;
u = lazyx[p];
v = lazyy[p];
w = lazyz[p];
for (LL i = 0; i < lazyro[rchild]; i++)
{
swp(v, w);
swp(u, v);
}
lazyx[rchild] = (lazyx[rchild] * (lazymul[p] % MOD) % MOD + u % MOD) % MOD;
lazyy[rchild] = (lazyy[rchild] * (lazymul[p] % MOD) % MOD + v % MOD) % MOD;
lazyz[rchild] = (lazyz[rchild] * (lazymul[p] % MOD) % MOD + w % MOD) % MOD;
if (lazymul[p] != 1)
{
if (!lazymul[lchild]) lazymul[lchild] = 1;
lazymul[lchild] = (lazymul[lchild] * (lazymul[p] % MOD)) % MOD;
if (!lazymul[rchild]) lazymul[rchild] = 1;
lazymul[rchild] = (lazymul[rchild] * (lazymul[p] % MOD)) % MOD;
}
lazyro[lchild] = (lazyro[lchild] + lazyro[p]) % 3;
lazyro[rchild] = (lazyro[rchild] + lazyro[p]) % 3;
lazyx[p] = 0;
lazyy[p] = 0;
lazyz[p] = 0;
lazymul[p] = 0;
lazyro[p] = 0;
}
void add(LL &p, LL start, LL end, LL l, LL r, LL a, LL b, LL c)
{
if (!p) p = ++cnt;
if (end <l || start>r) return;
if (start >= l && end <= r)
{
sumx[p] = (sumx[p] + (end - start + 1) % MOD * (a % MOD) % MOD) % MOD;
sumy[p] = (sumy[p] + (end - start + 1) % MOD * (b % MOD) % MOD) % MOD;
sumz[p] = (sumz[p] + (end - start + 1) % MOD * (c % MOD) % MOD) % MOD;
for (LL i = 0; i < lazyro[p]; i++)
{
swp(b, c);
swp(a, b);
}
lazyx[p] = (lazyx[p] + a % MOD) % MOD;
lazyy[p] = (lazyy[p] + b % MOD) % MOD;
lazyz[p] = (lazyz[p] + c % MOD) % MOD;
return;
}
if (lazyx[p] || lazyy[p] || lazyz[p] || lazymul[p] || lazyro[p]) pushdown(p, start, end);
LL mid = (start + end) >> 1;
if (mid >= l)
add(lson[p], start, mid, l, r, a, b, c);
if (mid < r)
add(rson[p], mid + 1, end, l, r, a, b, c);
sumx[p] = (sumx[lson[p]] % MOD + sumx[rson[p] % MOD]) % MOD;
sumy[p] = (sumy[lson[p]] % MOD + sumy[rson[p] % MOD]) % MOD;
sumz[p] = (sumz[lson[p]] % MOD + sumz[rson[p] % MOD]) % MOD;
}
void mul(LL &p, LL start, LL end, LL l, LL r, LL k)
{
if (!p) p = ++cnt;
if (end <l || start>r) return;
if (start >= l && end <= r)
{
if (!lazymul[p]) lazymul[p] = 1;
sumx[p] = (sumx[p] * (k % MOD)) % MOD;
sumy[p] = (sumy[p] * (k % MOD)) % MOD;
sumz[p] = (sumz[p] * (k % MOD)) % MOD;
lazyx[p] = (lazyx[p] * (k % MOD)) % MOD;
lazyy[p] = (lazyy[p] * (k % MOD)) % MOD;
lazyz[p] = (lazyz[p] * (k % MOD)) % MOD;
lazymul[p] = (lazymul[p] * (k % MOD)) % MOD;
return;
}
if (lazyx[p] || lazyy[p] || lazyz[p] || lazymul[p] || lazyro[p])
pushdown(p, start, end);
LL mid = (start + end) >> 1;
if (mid >= l)
mul(lson[p], start, mid, l, r, k);
if (mid < r)
mul(rson[p], mid + 1, end, l, r, k);
sumx[p] = (sumx[lson[p]] % MOD + sumx[rson[p] % MOD]) % MOD;
sumy[p] = (sumy[lson[p]] % MOD + sumy[rson[p] % MOD]) % MOD;
sumz[p] = (sumz[lson[p]] % MOD + sumz[rson[p] % MOD]) % MOD;
}
void rot(LL &p, LL start, LL end, LL l, LL r)
{
if (!p) p = ++cnt;
if (end<l || start>r) return;
if (start >= l && end <= r)
{
swp(sumx[p], sumy[p]);
swp(sumy[p], sumz[p]);
lazyro[p] ++;
lazyro[p] = lazyro[p] % 3;
return;
}
if (lazyx[p] || lazyy[p] || lazyz[p] || lazymul[p] || lazyro) pushdown(p, start, end);
LL mid = (start + end) >> 1;
if (mid >= l)
rot(lson[p], start, mid, l, r);
if (mid < r)
rot(rson[p], mid + 1, end, l, r);
sumx[p] = (sumx[lson[p]] % MOD + sumx[rson[p] % MOD]) % MOD;
sumy[p] = (sumy[lson[p]] % MOD + sumy[rson[p] % MOD]) % MOD;
sumz[p] = (sumz[lson[p]] % MOD + sumz[rson[p] % MOD]) % MOD;
}
void query(LL &p, LL start, LL end, LL l, LL r, LL &a, LL &b, LL &c)
{
if (!p) p = ++cnt;
if (end<l || start>r) return;
if (start >= l && end <= r)
{
a = (a + sumx[p] % MOD) % MOD;
b = (b + sumy[p] % MOD) % MOD;
c = (c + sumz[p] % MOD) % MOD;
return;
}
if (lazyx[p] || lazyy[p] || lazyz[p] || lazymul[p] || lazyro[p]) pushdown(p, start, end);
LL mid = (start + end) >> 1;
if (mid >= l)
query(lson[p], start, mid, l, r, a, b, c);
if (mid < r)
query(rson[p], mid + 1, end, l, r, a, b, c);
}
int main()
{
LL i, j;
//输入n,m
cin >> n >> m;
LL start = 1, end = n, root;
root = ++cnt;
for (i = 1; i <= m; i++)
{
LL type;
cin >> type;
LL l, r;
cin >> l >> r;
//动力增加
if (type == 1)
{
LL a, b, c;
cin >> a >> b >> c;
add(root, start, end, l, r, a, b, c);
}
//动力强化
else if (type == 2)
{
LL k;
cin >> k;
mul(root, start, end, l, r, k);
}
//动力转化
else if (type == 3)
{
rot(root, start, end, l, r);
}
//动力查询
else if (type == 4)
{
LL f = 0, d = 0, e = 0;
query(root, start, end, l, r, f, d, e);
cout << ((f % MOD) * (f % MOD) % MOD + (d % MOD) * (d % MOD) % MOD + (e % MOD) * (e % MOD) % MOD) % MOD << endl;
}
}
return 0;
}