代码源每日一题 div1 (601-607)
并行排序
[Link](并行排序 - 题目 - Daimayuan Online Judge)
思路
- 最长下降子序列
前面的数大于后面的数就会连一条边,我们可以把最长下降子序列搞出来假设长为 l e n len len,因为是递减的,子序列中每一个点都需要和前面的不同也需要和后面的不同,因此长为几就需要上多少个色,因为其他的下降子序列比最长下降子序列短,所以需要更少的颜色就可以将其涂抹掉,所以解就是最长下降子序列的长度。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e6 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int q[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n;
int len = 0;
int res = 0;
for (int i = 1; i <= n; i ++) {
int x; cin >> x;
int l = 0, r = len;
while (l < r) {
int mid = l + r + 1 >> 1;
if (q[mid] > x) l = mid;
else r = mid - 1;
}
len = max(len, r + 1);
q[r + 1] = x;
}
cout << len << '\n';
}
return 0;
}
丹钓战
[Link](丹钓战 - 题目 - Daimayuan Online Judge)
思路
- 模拟,离线,树状数组
假设我们查的整个区间,我们用个栈模拟一遍,最终成功的是什么样的呢?发现是当前这个点入栈后,这个点在栈底的那些点,推广到我们的区间,除了当前点在栈底的点还有什么的点成立呢?如果某个点下面的点的编号小于当前区间的左端点,那么这些点在这个区间里是不存在的,就等价于这个点在栈底了,所以这样的也成立。
因此我们可以模拟一遍,记录每个点下面最小的点的编号是多少特殊的没有的就记录为 0 0 0,查询的时候我们等价于查询某个区间里有多少个小于区间左端点的点,查询某个区间里小于某个数的个数我们可以用,离线,定个偏序用树状数组来搞,具体可参照 108 108 108数数这道题。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 5e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N], b[N];
PII c[N];
int tr[N];
int lowbit(int x) {
return x & -x;
}
void add(int x) {
for (; x <= n; x += lowbit(x)) tr[x] ++;
}
int sum(int x) {
int res = 0;
for (; x; x -= lowbit(x)) res += tr[x];
return res;
}
struct Node {
int l, r, h, id;
bool operator<(Node t) {
return h < t.h;
}
}q[N];
int res[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i <= n; i ++) cin >> b[i];
stack<int> stk;
for (int i = 1; i <= n; i ++) {
while (stk.size() && (a[stk.top()] == a[i] || b[stk.top()] <= b[i])) stk.pop();
if (stk.size()) c[i] = {stk.top() + 1, i};
else c[i] = {1, i};
stk.push(i);
}
sort(c + 1, c + 1 + n);
for(int i = 1; i <= m; i ++) cin >> q[i].l >> q[i].r, q[i].h = q[i].l, q[i].id = i;
sort(q + 1, q + 1 + m);
idx = 1;
for (int i = 1; i <= m; i ++) {
while (idx <= n && c[idx].x <= q[i].h) add(c[idx].y), idx ++;
res[q[i].id] = sum(q[i].r) - sum(q[i].l - 1);
}
for (int i = 1; i <= m; i ++)
cout << res[i] << '\n';
return 0;
}
删数
[Link](删数 - 题目 - Daimayuan Online Judge)
思路
- 倍增, d p dp dp
首先我们设 d i = a i − a i − 1 d_i=a_i-a_{i-1} di=ai−ai−1,那么我们的操作就等价于让两个相邻且相等的 d d d合并成 2 d 2d 2d。
我们对于每个数往后合并到哪我们是不确定的,因此可以 d p dp dp来做,倒着来做考虑 f [ i ] : 从 n 合 并 到 i 序 列 的 最 小 长 度 f[i]:从n合并到i序列的最小长度 f[i]:从n合并到i序列的最小长度,转移也很好判断我们找到所有 i i i可以合并到的 j j j, f [ i ] = m i n ( f [ j + 1 ] + 1 ) f[i]=min(f[j+1]+1) f[i]=min(f[j+1]+1),问题就是怎么快速找到当前这个数可以往后合并到的数。
发现每次合并只会让 d d d在原本的基础上 × 2 \times 2 ×2,因此我们可以将每个数分解成 p 2 k p2^k p2k( p p p是不能再往下分解的奇数),因此如果两个数 p p p不同那么一定不会合并,所以对于每个数只会和连续的 p p p相同的数合并。因此我们可以一段一段处理相同的 p p p。
对于某一段相同的 p p p,我们可以用倍增的思想来求,设 g [ i ] [ j ] : 当 前 在 i 往 后 合 并 出 来 一 个 2 j 的 下 一 个 位 置 是 哪 g[i][j]:当前在i往后合并出来一个2^j的下一个位置是哪 g[i][j]:当前在i往后合并出来一个2j的下一个位置是哪,对于 g [ i ] [ j ] = g [ g [ i ] [ j − 1 ] ] [ j − 1 ] g[i][j]=g[g[i][j-1]][j-1] g[i][j]=g[g[i][j−1]][j−1]如果成立的话,因此这样就可以快速的求出每个数往后可以合并到哪,然后就可以做上面的 d p dp dp了。
特殊的如果 d i = 0 d_i=0 di=0,只能连续的一个一个合并,特殊处理即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 3e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N], g[N][51], f[N], d[N], cnt[N];
void init() {
for (int i = 1; i <= n; i ++) f[i] = INF, cnt[i] = 0;
for (int i = 1; i <= n; i ++)
for (int j = 0; j <= 50; j ++)
g[i][j] = -1;
}
void solve() {
cin >> n;
init();
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i < n; i ++) d[i] = a[i + 1] - a[i];
for (int i = 1; i < n; i ++) {
if (!d[i]) d[i] = INF;
else {
while (d[i] % 2 == 0) cnt[i] ++, d[i] >>= 1;
}
}
for (int i = n - 1; i; i --) {
g[i][cnt[i]] = i + 1;
for (int j = cnt[i] + 1; j <= 50; j ++) {
if (g[i][j - 1] == -1) break;
if (d[g[i][j - 1]] != d[i]) break;
if (g[i][j - 1] >= n) break;
g[i][j] = g[g[i][j - 1]][j - 1];
}
}
f[1] = 1;
for (int i = 1; i < n; i ++) {
if (d[i] == INF && d[i + 1] == INF)
f[i + 1] = min(f[i + 1], f[i]);
else {
for (int j = 0; j <= 50; j ++)
if (g[i][j] != -1)
f[g[i][j]] = min(f[g[i][j]], f[i] + 1);
}
}
cout << f[n] << '\n';
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
solve();
}
return 0;
}
环的数量
[Link](环的数量 - 题目 - Daimayuan Online Judge)
思路
- 状压 d p dp dp
考虑状压 d p dp dp, f [ i ] [ s t a t e ] : 表 示 当 前 在 i 号 点 且 经 过 的 点 的 状 态 为 s t a t e 的 方 案 数 f[i][state]:表示当前在i号点且经过的点的状态为state的方案数 f[i][state]:表示当前在i号点且经过的点的状态为state的方案数,怎么不重不漏的的统计方案呢?我们可以这样统计,对于一个状态 s t a t e state state,我们只让每个走过的点和当前编号最小的点成环,这样就是补充不漏了的了,然后每个状态往下一个状态转移就是找到一个走过的点,往没走过的点走就可以。
由于图是无向图,因此每个环顺时针逆时针个记录一次,并且每条边也会被记录,所以最后应该为 r e s − m / 2 res-m/2 res−m/2。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 510, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
bool g[N][N];
LL f[1 << 20][20];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i ++) {
int x, y; cin >> x >> y;
x --, y --;
g[x][y] = g[y][x] = true;
}
for (int i = 0; i < n; i ++) f[1 << i][i] = 1;
LL res = 0;
for (int state = 1; state < 1 << n; state ++) {
int st = log2((state & -state));
for (int i = 0; i < n; i ++) {
if (!(state >> i & 1)) continue ;
if (g[i][st]) res += f[state][i];
for (int j = st + 1; j < n; j ++) {
if (!g[i][j]) continue ;
if (state >> j & 1) continue ;
f[state | (1 << j)][j] += f[state][i];
}
}
}
cout << (res - m) / 2 << '\n';
return 0;
}
Ayoub’s function
[Link](Ayoub’s function - 题目 - Daimayuan Online Judge)
思路
- 贪心
一共 n n n个数有 m m m个 1 1 1,相当于我们要将 n − m n-m n−m个 0 0 0分成不超过 m + 1 m+1 m+1份,我们知道总的子串数位 n ( 1 + n ) / 2 n(1+n)/2 n(1+n)/2,假设某一份为 x x x,那么这一份不合法的串有 x ( 1 + x ) / 2 → x / 2 + x 2 / 2 x(1+x)/2\to x/2+x^2/2 x(1+x)/2→x/2+x2/2,等价于我们有一些 x x x, x 1 / 2 + x 2 / 2 + . . . + x k / 2 + x 1 2 / 2 + x 2 2 / 2 + . . . + x k 2 / 2 x_1/2+x_2/2+...+x_k/2+x_1^2/2+x_2^2/2+...+x_k^2/2 x1/2+x2/2+...+xk/2+x12/2+x22/2+...+xk2/2,前一部分的和为 ( n − m ) / 2 (n-m)/2 (n−m)/2,后面是和一定的平方和我们要让这个东西最小,根据均值不等式,让他们尽量均衡即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
LL n, m, k;
int a[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T; cin >> T;
while (T --) {
cin >> n >> m;
LL sum = (1 + n) * n / 2;
n -= m;
LL t1 = n / (m + 1);
LL t2 = t1 + 1;
LL cnt1 = (m + 1 - (n % (m + 1)));
LL cnt2 = (m + 1 - cnt1);
cout << sum - cnt1 * (1 + t1) * t1 / 2 - cnt2 * (1 + t2) * t2 / 2 << '\n';
}
return 0;
}
最大权值划分
[Link](最大权值划分 - 题目 - Daimayuan Online Judge)
思路
- d p dp dp
可以发现每一段一定是单调的,如果不是我们每次从不单调的第一个位置直接断开,权值一定会更大。因此可以把所有的断点预处理出来,判断是上升的还是下降的,直接转移即可。
没有因为是单调的所以可以直接知道前面的最大或最小,写了个 s t st st表来搞,太臭了。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e6 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
LL f[N][2];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 2; i <= n; i ++) {
if (a[i] >= a[i - 1]) {
f[i][0] = max(f[i - 1][1], f[i - 1][0] + a[i] - a[i - 1]);
f[i][1] = max(f[i - 1][0], f[i - 1][1]);
}
else {
f[i][0] = max(f[i - 1][0], f[i - 1][1]);
f[i][1] = max(f[i - 1][1] + a[i - 1] - a[i], f[i - 1][0]);
}
}
cout << max(f[n][0], f[n][1]) << '\n';
return 0;
}
- s t st st表
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e6 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int sgn(int x) {
if (x == 0) return 0;
else if (x < 0) return -1;
else return 1;
}
LL f[N][2];
int fmx[N][21], fmn[N][21];
int query_mx(int u, int l, int r) {
if (l > r) return 0;
int j = log2(r - l + 1);
return max(fmx[l][j], fmx[r - (1 << j) + 1][j]);
}
int query_mn(int u, int l, int r) {
if (l > r) return 0;
int j = log2(r - l + 1);
return min(fmn[l][j], fmn[r - (1 << j) + 1][j]);
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
int op = 1;
for (int i = 1; i <= n; i ++)
fmx[i][0] = fmn[i][0] = a[i];
for (int j = 1; j <= 20; j ++)
for (int i = 1; i + (1 << j) - 1 <= n; i ++) {
fmx[i][j] = max(fmx[i][j - 1], fmx[i + (1 << (j - 1))][j - 1]);
fmn[i][j] = min(fmn[i][j - 1], fmn[i + (1 << (j - 1))][j - 1]);
}
vector<int> ve;
ve.push_back(1);
for (int i = 1; i <= n; i ++)
if (sgn(a[i] - a[i - 1]) * sgn(a[i + 1] - a[i]) <= 0)
ve.push_back(i);
if (ve.back() != n) ve.push_back(n);
int k = ve.size();
for (int i = 1; i <= k; i ++) {
int l = ve[i - 1], r = ve[i];
f[i][0] = max(f[i][0], max(f[i - 1][1] + query_mx(1, l, r) - query_mn(1, l, r), f[i - 1][0] + query_mx(1, l + 1, r) - query_mn(1, l + 1, r)));
f[i][1] = max(f[i][1], max(f[i - 1][1] + query_mx(1, l, r - 1) - query_mn(1, l, r - 1), f[i - 1][0] + query_mx(1, l + 1, r - 1) - query_mn(1, l + 1, r - 1)));
}
cout << max(f[k][0], f[k][1]) << '\n';
return 0;
}
括号序列
[Link](括号序列 - 题目 - Daimayuan Online Judge)
思路
- d p dp dp
将方案划按找最右端在哪来划分,设 f [ i ] : 最 右 端 在 i 的 合 法 括 号 序 列 个 数 f[i]:最右端在i的合法括号序列个数 f[i]:最右端在i的合法括号序列个数,首先 s t r [ i ] = = ) str[i]==) str[i]==),否则一定不成立,并且这个 ) ) )一定要匹配到它对应的那个 ( ( (,假设它匹配的左括号位于 j j j,则 [ j , i ] [j,i] [j,i]这一段一定是合法的一个,且 ( j , i ] (j,i] (j,i]不合法,然后前面的以 j − 1 j-1 j−1结尾的合法子串后面都可以接上这个,因此 f [ i ] = f [ j ] + 1 f[i]=f[j]+1 f[i]=f[j]+1。
先栈模拟一下,每个括号匹配的左边的括号,然后从左到右 d p dp dp一下即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e6 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
LL a[N], f[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
string str; cin >> str;
n = str.size();
str = " " + str;
stack<int> stk;
for (int i = 1; i <= n; i ++) {
if (!stk.size()) stk.push(i);
else if (str[stk.top()] == '(' && str[i] == ')') a[i] = stk.top(), stk.pop();
else stk.push(i);
}
for (int i = 1; i <= n; i ++)
if (str[i] == ')')
if (a[i])
f[i] = f[a[i] - 1] + 1;
LL res = 0;
for (int i = 1; i <= n; i ++)
res += f[i];
cout << res << '\n';
return 0;
}
- 二分
将 ( ( (赋为 1 1 1, ) ) )赋为 − 1 -1 −1,设 s : 为 原 序 列 的 前 缀 和 数 组 s:为原序列的前缀和数组 s:为原序列的前缀和数组,我们将方案按照左端点分类,对于每个 s i s_i si(保证 s i > s i − 1 s_i>s_{i-1} si>si−1),右边的一个点 j j j保证 [ i , j ] [i,j] [i,j]里的任何一个 s k s_k sk均 ≥ s i − 1 \ge s_{i-1} ≥si−1,这个东西我们发现 s j − s i − 1 s_j-s_{i-1} sj−si−1是不具有二段性的,但是 i i i这个点往后的区间里的最小值具有二段性,因此可以用 s t st st表搞出来区间最小值,然后二分查当前区间最小值是否 ≥ s i − 1 \ge s_{i-1} ≥si−1即可。
我们对于一个 i i i搞到一个最长合法区间 [ i , j ] [i,j] [i,j],这个区间合法的串就是右多少个位置 k k k满足 s k = = s i − 1 s_k==s_{i-1} sk==si−1,对于查询某个区间有多少个等于 k k k的数我们可以分成两部分, [ 1 , i − 1 ] [1,i-1] [1,i−1]小于的和 [ 1 , j ] [1,j] [1,j]小于的,减一下就可以,这个可以用一个 v e c t o r vector vector来存储然后从从前往后遍历这个串的时候动态的来算贡献即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e6 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int s[N];
struct Node {
int op, h;
};
int f[N][21];
int query(int l, int r) {
int j = log2(r - l + 1);
return min(f[l][j], f[r - (1 << j) + 1][j]);
}
vector<Node> q[N + 100];
int cnt[N * 2 + 10];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
string str;
cin >> str;
n = str.size();
for (int i = 1; i <= n; i ++)
s[i] = s[i - 1] + (str[i - 1] == '(' ? 1 : -1), f[i][0] = s[i];
for (int j = 1; j <= 20; j ++)
for (int i = 1; i + (1 << j) - 1 <= n; i ++)
f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
for (int i = 1; i <= n; i ++) {
if (s[i] < s[i - 1]) continue ;
int l = i, r = n, v = s[i - 1];
while (l < r) {
int mid = l + r + 1 >> 1;
if (query(i, mid) >= v) l = mid;
else r = mid - 1;
}
q[l].push_back({1, v});
q[i - 1].push_back({-1, v});
}
LL res = 0;
for (int i = 1; i <= n; i ++) {
cnt[N + s[i]] ++;
for (auto t : q[i])
res += (LL)t.op * cnt[N + t.h];
}
cout << res << '\n';
return 0;
}