2021CCPC东北赛题解ACDEIJKM
I. Takeaway
题意
有七种外卖,编号 1 ∼ 7 1\sim 7 1∼7,价格分别为 7 , 27 , 41 , 63 , 78 , 108 7,27,41,63,78,108 7,27,41,63,78,108.现有三种优惠券:①满 69 69 69减 15 15 15;②满 89 89 89减 30 30 30;③满 120 120 120减 50 50 50.每次只能用一张外卖券,一张外卖券只能用一次.
有 t ( 1 ≤ t ≤ 1 e 6 ) t\ \ (1\leq t\leq 1\mathrm{e}6) t (1≤t≤1e6)组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 7 ) n\ \ (1\leq n\leq 7) n (1≤n≤7),表示某人一次性点的外卖的数量.第二行输入 n n n个整数 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an,表示某人点的外卖的编号.
对每组测试数据,输出他至少需付多少钱.
代码 -> 2021CCPC东北赛-I(模拟)
int dish[10] = { 0,7,27,41,49,63,78,108 };
void solve() {
int n; cin >> n;
int sum = 0;
while (n--) {
int a; cin >> a;
sum += dish[a];
}
if (sum >= 120) sum -= 50;
else if (sum >= 89) sum -= 30;
else if (sum >= 69) sum -= 15;
cout << sum << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
E. Easy Math Problem
题意
有 t ( 1 ≤ t ≤ 4000 ) t\ \ (1\leq t\leq 4000) t (1≤t≤4000)组测试数据.每组测试数据输入一个整数 p ( 1 ≤ p ≤ 1 e 9 ) p\ \ (1\leq p\leq 1\mathrm{e}9) p (1≤p≤1e9).
对每组测试数据,求一个 p p p的倍数 k ( 1 ≤ k ≤ 1 e 18 ) s . t . k k\ \ (1\leq k\leq 1\mathrm{e}18)\ s.t.\ k k (1≤k≤1e18) s.t. k除自身外的所有正因数构成的元素个数不超过 1000 1000 1000的集合中的数之和为 k k k.第一行输出两个整数,分别表示 k k k和集合的元素个数 n n n.第二行输出 n n n个整数,表示 k k k除自身外的所有正因数构成的集合.
思路
注意到 1 + 2 + 3 = 6 1+2+3=6 1+2+3=6,两边乘 p p p即可.
代码 -> 2021CCPC东北赛-E(构造)
void solve() {
ll p; cin >> p;
cout << 6 * p << ' ' << 3 << endl;
cout << p << ' ' << 2 * p << ' ' << 3 * p << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
K. City
题意
有一张包含 n n n个节点和 m m m条边的无向图,每条边有一个权值.每进行一次伤害为 x x x的攻击时,所有边权 < x <x <x的边都会切断.问每次攻击后还有多少对节点连通.
有 t ( 1 ≤ t ≤ 10 ) t\ \ (1\leq t\leq 10) t (1≤t≤10)组测试数据.每组测试数据第一行输入三个整数 n , m , q ( 2 ≤ n ≤ 1 e 5 , 1 ≤ m , q ≤ 2 e 5 ) n,m,q\ \ (2\leq n\leq 1\mathrm{e}5,1\leq m,q\leq 2\mathrm{e}5) n,m,q (2≤n≤1e5,1≤m,q≤2e5),分别表示节点数、边数、询问数.接下来 m m m行每行输入三个整数 x , y , k ( 1 ≤ x , y ≤ n , 1 ≤ k ≤ 1 e 9 ) x,y,k\ \ (1\leq x,y\leq n,1\leq k\leq 1\mathrm{e}9) x,y,k (1≤x,y≤n,1≤k≤1e9),表示节点 x x x与 y y y间存在权值为 k k k的无向边.接下来 q q q行每行包含一个整数 x ( 1 ≤ x ≤ 1 e 9 ) x\ \ (1\leq x\leq 1\mathrm{e}9) x (1≤x≤1e9),表示询问进行一次伤害为 x x x的攻击时有多少对节点连通.
思路
显然可用并查集维护连通块,但并查集做删除操作不方便.考虑离线,先将所有边按边权降序排列,将所有询问按 x x x降序排列,则全程只需考虑加边.
含 n n n个节点的连通块对答案的贡献为 n ( n − 1 ) 2 \dfrac{n(n-1)}{2} 2n(n−1).合并包含 x x x、 y y y个节点的两连通块时,答案的净增量为 ( x + y ) ( x + y ) − 1 2 − x ( x − 1 ) 2 − y ( y − 1 ) 2 = x y \dfrac{(x+y)(x+y)-1}{2}-\dfrac{x(x-1)}{2}-\dfrac{y(y-1)}{2}=xy 2(x+y)(x+y)−1−2x(x−1)−2y(y−1)=xy.
代码 -> 2021CCPC东北赛-K(离线+并查集)
const int MAXN = 2e5 + 5;
int n, m, q; // 节点数、边数、询问数
struct Edge {
int u, v, w;
bool operator<(const Edge& B) { return w > B.w; }
}edges[MAXN];
pii ques[MAXN]; // 询问的x、编号
int fa[MAXN], siz[MAXN]; // 并查集的fa[]数组、集合的大小
ll ans[MAXN]; // 每个询问的答案
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
void solve() {
cin >> n >> m >> q;
for (int i = 0; i < m; i++) {
int x, y, z; cin >> x >> y >> z;
edges[i] = { x,y,z };
}
for (int i = 0; i < q; i++) {
int x; cin >> x;
ques[i] = { x,i };
}
for (int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1; // 初始化
sort(edges, edges + m);
sort(ques, ques + q, greater<pii>());
ll res = 0; // 每个询问的答案
int idx = 0; // 当前用到的边的下标
for (int i = 0; i < q; i++) { // 枚举询问
while (idx < m && edges[idx].w >= ques[i].first) {
int u = find(edges[idx].u), v = find(edges[idx].v);
if (u != v) {
res += (ll)siz[u] * siz[v];
fa[v] = u;
siz[u] += siz[v];
}
idx++;
}
ans[ques[i].second] = res;
}
for (int i = 0; i < q; i++) cout << ans[i] << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
M. Master of Shuangpin
题意
根据上表将输入的拼音转为双拼.规则:①若拼音长度为 1 1 1,则需重复一遍,保证长度为 2 2 2;②若拼音长度为 2 2 2,输出它本身;③对类似于ang的拼音,应先输出其首字母,再查表输出第二个字母.
有多组测试数据.每组测试数据输入一行包含若干个拼音.数据保证测试数据的组数不超过 1000 1000 1000,每组测试数据中的拼音总数不超过 500 500 500,且拼音中不含’v’.
思路
模拟即可.注意长度为 3 3 3的拼音有如下三种情况:jia、zha、ang.
代码 -> 2021CCPC东北赛-M(模拟)
map<string, string> mp = {
{"q","q"}, {"iu","q"},{"w","w"}, {"ei","w"}, {"r","r"}, {"uan","r"},{"t","t"}, {"ue","t"},
{"y","y"}, {"un","y"},{"u","u"}, {"sh","u"}, {"i","i"}, {"ch","i"},{"o","o"}, {"uo","o"},
{"p","p"}, {"ie","p"},{"a","a"}, {"s","s"}, {"ong","s"}, {"iong","s"},{"d","d"}, {"ai","d"},
{"f","f"}, {"en","f"},{"g","g"}, {"eng","g"}, {"h","h"}, {"ang","h"},{"j","j"}, {"an","j"},
{"k","k"}, {"uai","k"},{"ing","k"}, {"l","l"}, {"uang","l"}, {"iang","l"},{"z","z"}, {"ou","z"},
{"x","x"}, {"ia","x"},{"ua","x"}, {"c","c"}, {"ao","c"}, {"v","v"},{"zh","v"}, {"ui","v"},
{"b","b"}, {"in","b"},{"n","n"}, {"iao","n"}, {"m","m"}, {"ian","m"}, {"e","e"}
};
void solve() {
string line;
while (getline(cin, line)) {
stringstream ss(line);
string s;
while (ss >> s) {
if (s.length() == 1) cout << s << s << ' ';
else if (s.length() == 2) cout << s << ' ';
else {
if (mp.count(s.substr(0, 3))) {
if (s.length() == 3) { // 特判ang
cout << s[0] << mp[s.substr(0, 3)] << ' ';
continue;
}
cout << mp[s.substr(0, 3)];
s = s.substr(3);
}
else if (mp.count(s.substr(0, 2))) {
cout << mp[s.substr(0, 2)];
s = s.substr(2);
}
else {
cout << s[0];
s = s.substr(1);
}
cout << mp[s] << ' ';
}
}
cout << endl;
}
}
int main() {
solve();
}
C. Vertex Deletion
题意
给定一棵包含编号 1 ∼ n 1\sim n 1∼n的 n n n个节点的树,其中节点 1 1 1为根节点.每次操作可删除一个节点.好的操作定义为删除节点后没有孤立的节点,即每个节点至少存在另一个节点与之连通.求好的操作的个数,答案对 998244353 998244353 998244353取模.
有 t t t组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 1 e 6 ) n\ \ (1\leq n\leq 1\mathrm{e}6) n (1≤n≤1e6).接下来 ( n − 1 ) (n-1) (n−1)行每行输入两个整数 u , v ( 1 ≤ u , v ≤ n , u ≠ v ) u,v\ \ (1\leq u,v\leq n,u\neq v) u,v (1≤u,v≤n,u=v),表示节点 u u u与 v v v间存在边.数据保证所有测试数据的 n n n之和不超过 1 e 6 1\mathrm{e}6 1e6.
思路
d p [ u ] [ 0 ] dp[u][0] dp[u][0]表示删除节点 u u u; d p [ u ] [ 1 ] dp[u][1] dp[u][1]表示不删除节点 u u u,但删去所有儿子节点; d p [ u ] [ 2 ] dp[u][2] dp[u][2]表示不删除节点 u u u,且至少保留一个儿子节点.最终答案 d p [ 1 ] [ 0 ] + d p [ 1 ] [ 2 ] dp[1][0]+dp[1][2] dp[1][0]+dp[1][2].
对 d p [ u ] [ 0 ] dp[u][0] dp[u][0],无需考虑节点 u u u是否符合条件;对 d p [ u ] [ 1 ] dp[u][1] dp[u][1],为使得节点 u u u符合条件,需保留 u u u的父节点;对 d p [ u ] [ 2 ] dp[u][2] dp[u][2],节点 u u u始终满足条件.
设节点 u u u的一个子节点为 v v v.
①若删除 u u u,为使得 v v v符合条件,可删除 v v v或至少保留 v v v的一个子节点,状态转移方程: d p [ u ] [ 0 ] = ∏ e d g e < u , v > ( d p [ v ] [ 0 ] + d p [ v ] [ 2 ] ) \displaystyle dp[u][0]=\prod_{edge<u,v>}(dp[v][0]+dp[v][2]) dp[u][0]=edge<u,v>∏(dp[v][0]+dp[v][2]).
②若不删除 u u u且删除所有儿子节点,则 v v v一定被删除,状态转移方程: d p [ u ] [ 1 ] = ∏ e d g e < u , v > d p [ v ] [ 0 ] \displaystyle dp[u][1]=\prod_{edge<u,v>}dp[v][0] dp[u][1]=edge<u,v>∏dp[v][0].
③若不删除 u u u且至少保留一个儿子节点,考虑容斥,状态转移方程: d p [ u ] [ 2 ] = ∏ e d g e < u , v > ( d p [ v ] [ 0 ] + d p [ v ] [ 1 ] + d p [ v ] [ 2 ] ) − d p [ u ] [ 1 ] \displaystyle dp[u][2]=\prod_{edge<u,v>}(dp[v][0]+dp[v][1]+dp[v][2])-dp[u][1] dp[u][2]=edge<u,v>∏(dp[v][0]+dp[v][1]+dp[v][2])−dp[u][1].
代码 -> 2021CCPC东北赛-C(树形DP)
const int MAXN = 2e5 + 5;
const int MOD = 998244353;
int n;
vi edges[MAXN];
// dp[u][0]表示删除节点u
// dp[u][1]表示不删除节点u,但删去所有儿子节点
// dp[u][2]表示不删除节点u,且至少保留一个儿子节点
int dp[MAXN][3];
void dfs(int u, int fa) { // 当前节点、前驱节点
dp[u][0] = dp[u][1] = dp[u][2] = 1; // 乘法,初始化为1
for (auto v : edges[u]) {
if (v == fa) continue;
dfs(v, u); // 递归求子树信息
dp[u][0] = ((ll)dp[v][0] + dp[v][2]) % MOD * dp[u][0] % MOD;
dp[u][1] = (ll)dp[u][1] * dp[v][0] % MOD;
dp[u][2] = ((ll)dp[v][0] + dp[v][1] + dp[v][2]) % MOD * dp[u][2] % MOD;
}
dp[u][2] = ((dp[u][2] - dp[u][1]) % MOD + MOD) % MOD;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) edges[i].clear();
for (int i = 1; i < n; i++) {
int u, v; cin >> u >> v;
edges[u].push_back(v), edges[v].push_back(u);
}
dfs(1, -1); // 从根节点开始搜,根节点无前驱节点
cout << (dp[1][0] + dp[1][2]) % MOD << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
A. Matrix
题意
将 1 ∼ n 2 1\sim n^2 1∼n2这 n 2 n^2 n2个数填入 n × n n\times n n×n的矩阵,每个数只能用一次.对每个固定的方案,令 a i a_i ai为第 i ( 1 ≤ i ≤ n ) i\ \ (1\leq i\leq n) i (1≤i≤n)行的最小值,集合 S = { a 1 , ⋯ , a n } ⋂ { 1 , ⋯ , n } S=\{a_1,\cdots,a_n\}\bigcap\{1,\cdots,n\} S={a1,⋯,an}⋂{1,⋯,n}.求所有填法的集合 S S S大小之和,即 ∑ ∣ S ∣ \displaystyle\sum |S| ∑∣S∣,答案对 998244353 998244353 998244353取模.
有 t ( 1 ≤ t ≤ 30 ) t\ \ (1\leq t\leq 30) t (1≤t≤30)组测试数据.每组测试数据输入一个整数 n ( 1 ≤ n ≤ 5000 ) n\ \ (1\leq n\leq 5000) n (1≤n≤5000).
思路
显然只需考虑 1 ∼ n 1\sim n 1∼n作为某行的最小值时对答案的贡献.以 1 1 1作为某行的最小值为例,先将其放到某行(不妨设为第一行),有 n n n种情况.在剩下的 ( n 2 − 1 ) (n^2-1) (n2−1)个数中选 ( n − 1 ) (n-1) (n−1)个 > 1 >1 >1的数放在第一行,有 C n 2 − 1 n − 1 C_{n^2-1}^{n-1} Cn2−1n−1种情况.第一行的数和其他行的数全排列,有 n ! ( n 2 − n ) ! n!(n^2-n)! n!(n2−n)!种情况.显然 a n s = n ⋅ n ! ⋅ ( n 2 − n ) ! ⋅ ∑ i = 1 n C n 2 − 1 n − i ans=n\cdot n!\cdot (n^2-n)!\cdot \displaystyle \sum_{i=1}^n C_{n^2-1}^{n-i} ans=n⋅n!⋅(n2−n)!⋅i=1∑nCn2−1n−i.
代码 -> 2021CCPC东北赛-A(组合计数)
const int MAXN = 5005 * 5005;
const int MOD = 998244353;
int fac[MAXN], ifac[MAXN];
void init() { // 预处理阶乘及其逆元
fac[0] = 1;
for (int i = 1; i < MAXN; i++) fac[i] = (ll)fac[i - 1] * i % MOD;
ifac[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2, MOD);
for (int i = MAXN - 1; i; i--) ifac[i - 1] = (ll)ifac[i] * i % MOD;
}
int C(int n, int m) { // C(n,m)
if (n < m) return 0;
else return (ll)fac[n] * ifac[m] % MOD * ifac[n - m] % MOD;
}
void solve() {
init();
CaseT{
int n; cin >> n;
int ans = 0;
for (int i = 1; i <= n; i++) ans = ((ll)ans + C(n * n - i, n - 1)) % MOD;
ans = (ll)ans * n % MOD * fac[n] % MOD * fac[n * n - n] % MOD;
cout << ans << endl;
}
}
int main() {
solve();
}
J. Transform
题意
给定两点 ( A , B , C ) (A,B,C) (A,B,C)和 ( x , y , z ) (x,y,z) (x,y,z).设过点 ( A , B , C ) (A,B,C) (A,B,C)和 ( 0 , 0 , 0 ) (0,0,0) (0,0,0)的直线为 L L L.将 ( x , y , z ) (x,y,z) (x,y,z)绕 L L L旋转角度 r r r和 − r -r −r分别得到点 P P P和 Q Q Q,输出其中 z z z坐标较大的点的坐标,误差不超过 1 e − 6 1\mathrm{e}-6 1e−6.数据保证答案唯一.
有 t ( 1 ≤ t ≤ 5 e 4 ) t\ \ (1\leq t\leq 5\mathrm{e}4) t (1≤t≤5e4)组测试数据.每组测试数据第一行输入七个整数 A , B , C , x , y , z , r ( 1 ≤ A , B , C , x , y , z ≤ 100 , 1 ≤ r < 180 ) A,B,C,x,y,z,r\ \ (1\leq A,B,C,x,y,z\leq 100,1\leq r<180) A,B,C,x,y,z,r (1≤A,B,C,x,y,z≤100,1≤r<180).
代码 -> 2021CCPC东北赛-J(三维旋转公式)
struct Point {
double x, y, z;
Point(double _x, double _y, double _z) :x(_x), y(_y), z(_z) {}
};
Point rotate(Point P, Point Q, double theta) { // 向量P绕向量OQ旋转theta弧度
double len = sqrt(Q.x * Q.x + Q.y * Q.y + Q.z * Q.z);
double tmpx = Q.x / len, tmpy = Q.y / len, tmpz = Q.z / len;
double resx = (tmpx * tmpx * (1 - cos(theta)) + cos(theta)) * P.x
+ (tmpx * tmpy * (1 - cos(theta)) - tmpz * sin(theta)) * P.y
+ (tmpx * tmpz * (1 - cos(theta)) + tmpy * sin(theta)) * P.z;
double resy = (tmpy * tmpx * (1 - cos(theta)) + tmpz * sin(theta)) * P.x
+ (tmpy * tmpy * (1 - cos(theta)) + cos(theta)) * P.y
+ (tmpy * tmpz * (1 - cos(theta)) - tmpx * sin(theta)) * P.z;
double resz = (tmpz * tmpx * (1 - cos(theta)) - tmpy * sin(theta)) * P.x
+ (tmpz * tmpy * (1 - cos(theta)) + tmpx * sin(theta)) * P.y
+ (tmpz * tmpz * (1 - cos(theta)) + cos(theta)) * P.z;
return Point(resx, resy, resz);
}
void solve() {
int A, B, C, x, y, z, r; cin >> A >> B >> C >> x >> y >> z >> r;
Point P(x, y, z), Q(A, B, C);
Point ans1 = rotate(P, Q, r * pi / 180);
Point ans2 = rotate(P, Q, -r * pi / 180);
if (cmp(ans1.z, ans2.z) > 0) cout << fixed << setprecision(12) << ans1.x << ' ' << ans1.y << ' ' << ans1.z << endl;
else cout << fixed << setprecision(12) << ans2.x << ' ' << ans2.y << ' ' << ans2.z << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
D. Lowbit
题意 ( 7 s 7\ \mathrm{s} 7 s)
维护一个序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an,支持如下两种操作:
① 1 l r 1\ l\ r 1 l r,表示所有 a i + = l o w b i t ( a i ) ( 1 ≤ l ≤ i ≤ r ≤ n ) a_i+=lowbit(a_i)\ \ (1\leq l\leq i\leq r\leq n) ai+=lowbit(ai) (1≤l≤i≤r≤n).
② 2 l r 2\ l\ r 2 l r,表示询问 ∑ i = l r a i \displaystyle\sum_{i=l}^r a_i i=l∑rai,答案对 998244353 998244353 998244353取模.
有 t ( 1 ≤ t ≤ 20 ) t\ \ (1\leq t\leq 20) t (1≤t≤20)组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n (1≤n≤1e5),表示序列长度.第二行输入 n n n个整数 a 1 , ⋯ , a n ( 1 ≤ a i < 998244353 ) a_1,\cdots,a_n\ \ (1\leq a_i<998244353) a1,⋯,an (1≤ai<998244353).第三行输入一个整数 m ( 1 ≤ m ≤ 1 e 5 ) m\ \ (1\leq m\leq 1\mathrm{e}5) m (1≤m≤1e5),表示操作个数.接下来 m m m行每行输入一个操作,格式如上.
思路
注意到二进制数为$100\cdots 的形式时 , 的形式时, 的形式时,+=lowbit 操作退化为 操作退化为 操作退化为=2$.当二进制数为 1 ⋯ 100 ⋯ 1\cdots 100\cdots 1⋯100⋯的形式时, + = l o w b i t +=lowbit +=lowbit操作会使得低位的 1 1 1向高位移动,若干次操作后低位的 1 1 1与高位的 1 1 1相邻,此时再 + = l o w b i t +=lowbit +=lowbit会发生进位,变为$100\cdots 的形式 . 综上 , 的形式.综上, 的形式.综上,lowbit 操作在不超过 操作在不超过 操作在不超过O(\log a) 次后都退化为 次后都退化为 次后都退化为=2 操作 , 只需在节点上开一个变量 操作,只需在节点上开一个变量 操作,只需在节点上开一个变量flag$记录当前区间内是否所有数的二进制表示都是$100\cdots 的形式 , 若是则按区间乘操作处理 , 打上 l a z y 标记后返回 ; 否则暴力修改叶子节点 , 每次修改后更新 的形式,若是则按区间乘操作处理,打上lazy标记后返回;否则暴力修改叶子节点,每次修改后更新 的形式,若是则按区间乘操作处理,打上lazy标记后返回;否则暴力修改叶子节点,每次修改后更新flag$.
注意 + = l o w b i t +=lowbit +=lowbit操作未退化前更新区间和不能取模,否则会影响对操作是否已退化的判断.
代码 -> 2021CCPC东北赛-D(势能线段树)
const int MAXN = 1e5 + 5;
const int MOD = 998244353;
int n; // 序列长度
int a[MAXN];
struct Node {
int l, r; // 区间端点
ll sum; // 区间和
bool flag; // 记录当前区间内所有数的二进制表示是否都是100...的形式
ll lazy; // 区间乘懒标记
}SegT[MAXN << 2];
bool check(int x) { return x == lowbit(x); } // 判断数的二进制表示是否是100...的形式
void push_up(int u) {
SegT[u].sum = (SegT[u << 1].sum + SegT[u << 1 | 1].sum) % MOD;
SegT[u].flag = SegT[u << 1].flag && SegT[u << 1 | 1].flag;
}
void build(int u, int l, int r) {
SegT[u] = { l,r,0,false,1 };
if (l == r) {
SegT[u].sum = a[l];
SegT[u].flag = check(a[l]);
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
push_up(u);
}
void push_down(int u) {
if (SegT[u].lazy > 1) {
SegT[u << 1].lazy = SegT[u << 1].lazy * SegT[u].lazy % MOD;
SegT[u << 1].sum = SegT[u << 1].sum * SegT[u].lazy % MOD;
SegT[u << 1 | 1].lazy = SegT[u << 1 | 1].lazy * SegT[u].lazy % MOD;
SegT[u << 1 | 1].sum = SegT[u << 1 | 1].sum * SegT[u].lazy % MOD;
SegT[u].lazy = 1; // 注意清空为1
}
}
void modify(int u, int l, int r) {
if (SegT[u].l == SegT[u].r) { // 暴力修改叶子节点
if (SegT[u].flag) SegT[u].sum = SegT[u].sum * 2 % MOD;
else {
SegT[u].sum += lowbit(SegT[u].sum); // 注意此处不能取模
SegT[u].flag = check(SegT[u].sum);
}
return;
}
if (l <= SegT[u].l && SegT[u].r <= r) {
if (SegT[u].flag) { // 已退化为区间乘
SegT[u].lazy = SegT[u].lazy * 2 % MOD;
SegT[u].sum = SegT[u].sum * 2 % MOD;
return;
}
}
push_down(u);
int mid = SegT[u].l + SegT[u].r >> 1;
if (l <= mid) modify(u << 1, l, r);
if (r > mid) modify(u << 1 | 1, l, r);
push_up(u);
}
int query(int u, int l, int r) { // 询问区间和[l,r]
if (l <= SegT[u].l && SegT[u].r <= r) return SegT[u].sum % MOD;
push_down(u);
int mid = SegT[u].l + SegT[u].r >> 1;
int res = 0;
if (l <= mid) res = ((ll)res + query(u << 1, l, r)) % MOD;
if (r > mid) res = ((ll)res + query(u << 1 | 1, l, r)) % MOD;
return res;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
CaseT{
int op,l,r; cin >> op >> l >> r;
if (op == 1) modify(1, l, r);
else cout << query(1, l, r) << endl;
}
}
int main() {
CaseT // 单测时注释掉该行
solve();
}