数据结构 21 问
基本数据结构基本功,其中的部分题目模拟较大,注意细节。
主要包括队列,栈,链表,堆,trie,树,表达式问题,单调性问题,基本算法综合,哈希,KMP
供后期复习的打卡,刷题,训练的代码。
注:对于一些复杂的推导单独写题解,其他的就简单写写思路。
双端队列,数组队列
#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 1010, M = 1000010;
int n;
queue<int> q[N];
int st[M];
int ith[N], cnt = 0, hh = 1;
void clear(queue<int> &q)
{
queue<int> et;
swap(et,q);
}
int main()
{
int t, T = 1;
while(cin >> t && t)
{
hh = 1, cnt = 0;
for(int i = 1; i <= t; i ++ )
{
int m; cin >> m;
while(m -- )
{
int x; cin >> x;
st[x] = i;
}
}
printf("Scenario #%d\n", T ++ );
string op;
while(cin >> op)
{
if(op == "STOP") break;
if(op == "ENQUEUE")
{
int x; cin >> x;
if(q[st[x]].size() == 0) ith[ ++ cnt] = st[x];
q[st[x]].push(x);
}
else
{
bool flag = false;
for(int i = hh; i <= cnt && !flag; i ++ )
{
int j = ith[i];
if(q[j].size())
{
cout << q[j].front() << endl; q[j].pop();
if(q[j].size() == 0) hh ++ ;
flag = true;
}
}
}
}
puts("");
for(int i = 1; i <= t; i ++ )
while(!q[i].empty()) q[i].pop();
}
return 0;
}
双栈,前缀和
#include <iostream>
#include <cstring>
#include <stack>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
stack<int> l, r;
int s[N], f[N];
int main()
{
memset(f, -0x3f, sizeof f);
int T; cin >> T;
while(T -- )
{
char op; cin >> op;
if(op == 'I')
{
int x; cin >> x;
l.push(x);
int n = l.size();
s[n] = s[n - 1] + x;
f[n] = max(f[n - 1], s[n]);
}
else if(op == 'L')
{
if(l.size()) r.push(l.top()), l.pop();
}
else if(op == 'R')
{
if(r.size()) l.push(r.top()), r.pop();
int n = l.size();
s[n] = s[n - 1] + l.top();
f[n] = max(f[n - 1], s[n]);
}
else if(op == 'Q')
{
int k; cin >> k;
cout << f[k] << endl;
}
else
{
if(l.size()) l.pop();
}
}
return 0;
}
dfs
千万不要误以为是全排列,全排列中的一些排列在进出栈中是不合法的。
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
vector<int> path;
stack<int> p;
int n;
int cnt = 0;
void dfs(int u)
{
if(cnt == 20) return;
if(path.size() == n)
{
for(int i = 0; i < path.size(); i ++ )
cout << path[i];
puts(""); cnt ++ ;
return;
}
if(p.size())
{
path.push_back(p.top());
p.pop();
dfs(u);
p.push(path.back());
path.pop_back();
}
if(u <= n)
{
p.push(u);
dfs(u + 1);
p.pop();
}
}
int main()
{
cin >> n;
dfs(1);
return 0;
}
简单的双栈同步模拟(一个存min值,一个存数值)
class MinStack {
public:
/** initialize your data structure here. */
stack<int> s;
stack<int> imin;
MinStack() {
}
void push(int x) {
s.push(x);
if(imin.empty() || imin.top() >= x) imin.push(x);
}
void pop() {
if(s.top() == imin.top()) imin.pop();
s.pop();
}
int top() {
return s.top();
}
int getMin() {
return imin.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
将原序列字段和转换为前缀和数组的相减;
得到\(s_i - s_{j-1}\),每次遍历i的位置,然后在m+1长度的区间内找出最小的\(s_j\)使得字段和最大。
这一性质与单调队列的滑动窗口性质相同。
#include <iostream>
#include <deque>
using namespace std;
const int N = 300010;
int a[N], s[N];
deque<int> q;
int main()
{
int n, m; cin >> n >> m;
for(int i = 1; i <= n; i ++ )
cin >> a[i], s[i] = s[i - 1] + a[i];
int res = -2e9;
q.push_back(0);
for(int i = 1; i <= n; i ++ )
{
if(q.size() && i - q.front() > m) q.pop_front();
res = max(res ,s[i] - s[q.front()]);
while(q.size() && s[q.back()] >= s[i]) q.pop_back();
q.push_back(i);
}
cout << res;
return 0;
}
首先找出矩形的性质。
根据数据范围可知,四重循环的暴力是会爆的。
那么我们可以以每一行为基准线,把每一行转换为直方图中的最大矩形问题,即可。
附上两题的代码。
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 100010;
ll a[N];
ll l[N], r[N], s[N], top = 0;
int main()
{
int n;
while(cin >> n && n)
{
for(int i = 1; i <= n; i ++ ) scanf("%lld", &a[i]);
a[0] = -1, a[n + 1] = -1;
top = 0; s[top] = 0;
for(int i = 1; i <= n; i ++ )
{
while(a[s[top]] >= a[i]) top -- ; // 跑到不能再拓宽为止
l[i] = s[top];
s[ ++ top ] = i;
}
top = 0; s[top] = n + 1;
for(int i = n; i ; i -- )
{
while(a[s[top]] >= a[i]) top -- ;
r[i] = s[top];
s[ ++ top ] = i;
}
ll res = 0;
for(int i = 1; i <= n; i ++ )
res = max(res, (r[i] - l[i] - 1) * a[i]);
printf("%lld\n", res);
}
return 0;
}
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int g[N][N];
int l[N], r[N]; //定义i点的左右最远距离
int s[N], top = 0;
void init(int a[])
{
top = 0;
s[top] = 0;
for(int j = 1; j <= m; j ++ )
{
while(a[s[top]] >= a[j]) top -- ;
l[j] = s[top] + 1;
s[ ++ top] = j;
}
top = 0;
s[top] = m + 1;
for(int j = m; j; j --)
{
while(a[s[top]] >= a[j]) top -- ;
r[j] = s[top] - 1;
s[ ++ top] = j;
}
}
int solve(int h[]) //分解成行内的直方图矩形问题
{
init(h);
int res = 0;
for(int j = 1; j <= m; j ++ ) // r是正向的,要镜像一下
res = max(res, (r[j] - l[j] + 1) * h[j]);
return res;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++ )
{
g[i][0] = g[i][m + 1] = -1;
for(int j = 1; j <= m; j ++ )
{
char t; cin >> t;
if(t == 'F') g[i][j] = g[i - 1][j] + 1;
else g[i][j] = 0;
}
}
int res = 0;
for(int i = 1; i <= n; i ++ )
res = max(res, solve(g[i]));
cout << res * 3;
return 0;
}
题意十分简单,没有复杂的性质。
考察对数据结构的应用和代码的基本功。
下面给出两种方法。
100 pts
平衡树(set)
#include <iostream>
#include <cmath>
#include <set>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n;
set<PII> s;
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++ )
{
int x; cin >> x;
s.insert({x, i});
set<PII>::iterator it = s.find({x, i});
if(i == 1) continue;
PII res = {2e9, -1};
if( ++ it != s.end())
res = {(*it).first - x, (*it).second};
it -- ;
if(it -- != s.begin() && res.first >= x - (*it).first)
res = {x - (*it).first, (*it).second};
cout << res.first << ' ' << res.second << endl;
}
return 0;
}
链表
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
typedef pair<ll, int> PII;
int l[N], r[N], p[N]; //邻接表
PII a[N]; int n;
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++ ) cin >> a[i].first, a[i].second = i;
sort(a + 1, a + n + 1);
a[0].first = -4e9, a[n + 1].first = 4e9; // 哨兵要开大, 超过两倍的(2 * 2e9)
for(int i = 1; i <= n; i ++ )
{
l[i] = i - 1 , r[i] = i + 1;
p[a[i].second] = i;
}
PII res[N];
for(int i = n; i ; i -- ) // 倒序,因为每次查询的是i往前的数
{
int j = p[i]; int pre = l[j], ne = r[j];
ll b = (ll)a[j].first - a[pre].first;
ll c = (ll)a[ne].first - a[j].first;
if(b <= c) res[i] = {b, a[pre].second};
else res[i] = {c, a[ne].second};
l[ne] = pre, r[pre] = ne; // 删除该节点
}
for(int i = 2; i <= n; i ++ ) cout << res[i].first << ' ' << res[i].second << endl;
return 0;
}
首先分析性质:
当$ 0 < i < j < k , a[k] < a[i] < a[j],那么i,j一定不能在同一个栈中$
这一点的证明建议自推一遍。
那么这么多的关系,只能转化为图论来解决。因为存在两个阵营,且阵营内部没有矛盾,所以可以判二分图,证明是否可以双栈排序。
然后就是优先级问题。网上很多的代码都是有缺陷的,这个是100 pts。
#include <iostream>
#include <cstring>
#include <stack>
using namespace std;
const int N = 1010;
int f[N]; // 表示从i到n中的最小值
int a[N], n;
stack<int> s[3];
bool g[N][N];
int color[N];
bool dfs(int u, int c)
{
color[u] = c;
for(int i = 1; i <= n; i ++ )
if(g[u][i])
{
if(color[i] == c) return false;
if(color[i] == -1 && !dfs(i, 3 - c)) return false;
}
return true;
}
int ith = 1;
// 分配站栈中的操作(判断,修改)
bool check(int k)
{
return s[k].size() && s[k].top() == ith;
}
void pop(int k)
{
ith ++ ;
s[k].pop();
char op = k == 1 ? 'b' : 'd';
cout << op << ' ';
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
f[n + 1] = 2e9;
for(int i = n; i >= 0; i -- ) f[i] = min(f[i + 1], a[i]);
memset(g, false, sizeof g);
for(int i = 1; i <= n; i ++ )
for(int j = i + 1; j <= n; j ++ )
if(a[i] < a[j] && f[j + 1] < a[i]) // 表示存在i,j,k这个情况
g[i][j] = g[j][i] = true;
bool is_success = true;
memset(color, -1, sizeof color);
for(int i = 1; i <= n; i ++ )
if(color[i] == -1 && !dfs(i, 1))
{
is_success = false;
break;
}
if(!is_success)
{
puts("0");
return 0;
}
// 方案查询(重点在于优先级问题)
for(int i = 1; i <= n; i ++ )
{
int c = color[i], x = a[i];
if(c == 2) // 进二号栈之前必须先尽可能的出一号栈
while(check(1)) pop(1);
// 新进的数字不满足该栈的单调递减性,但是必须塞入,所以先弹出s1,s2
while(s[c].size() && s[c].top() < x)
{
if(!check(c)) pop(3 - c);
else pop(c);
}
if(c == 2) // 同上
while(check(1)) pop(1);
s[c].push(x);
char op = c == 1 ? 'a' : 'c';
cout << op << ' ';
}
// 剩下的目标就是弹出所有数字
while(s[1].size())
{
if(!check(1)) pop(2);
else pop(1);
}
while(s[2].size()) pop(2);
return 0;
}
超棒的题目!!!
这道题综合考察了性质和单调性,队列的应用就是小事了,主要是从局部到整体考虑。
这个性质藏的很隐蔽,具体写了题解。
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010;
typedef long long ll;
ll n, m, q, u, v, t;
ll a[N];
queue<ll> q1, q2, q3;
int get(int dt)
{
ll a = -1, b = -1, c = -1;
if(q1.size()) a = q1.front() + q * dt;
if(q2.size()) b = q2.front() + q * dt;
if(q3.size()) c = q3.front() + q * dt;
ll res = max(a, max(b, c));
if(res == a) q1.pop();
else if(res == b) q2.pop();
else if(res == c) q3.pop();
return res;
}
int main()
{
cin >> n >> m >> q >> u >> v >> t;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
sort(a + 1, a + n + 1), reverse(a + 1, a + n + 1);
for(int i = 1; i <= n; i ++ ) q1.push(a[i]);
for(int i = 1; i <= m; i ++ )
{
ll x = get(i - 1);
if(i % t == 0) cout << x << ' ';
ll a = x * u / v; ll b = x - a;
q2.push(a - i * q);
q3.push(b - i * q);
}
cout << endl;
for(int i = 1; i <= n + m; i ++ )
{
ll x = get(m);
if(i % t == 0) cout << x << ' ';
}
return 0;
}
kmp模板题,纯字符串匹配。
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
char p[N], s[N * 10];
int n, m;
int ne[N]; // 最大前缀和后缀的匹配表
int main()
{
cin >> n >> p + 1 >> m >> s + 1;
for(int i = 2, j = 0; i <= n; i ++ )
{
while(j && p[i] != p[j + 1]) j = ne[j];
if(p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
for(int i = 1, j = 0; i <= m; i ++ )
{
while(j && s[i] != p[j + 1]) j = ne[j];
if(s[i] == p[j + 1]) j ++ ;
if(j == n)
{
cout << i - n << ' ';
j = ne[j];
}
}
return 0;
}
kmp变式题,需证明循环节与主串的关系
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1000010;
int ne[N];
char s[N];
int n;
int main()
{
int T = 1;
while(cin >> n && n)
{
printf("Test case #%d\n", T ++ );
memset(ne, 0, sizeof ne);
cin >> s + 1;
for(int i = 2, j = 0; i <= n; i ++ )
{
while(j && s[i] != s[j + 1]) j = ne[j];
if(s[i] == s[j + 1]) j ++ ;
ne[i] = j;
}
for(int i = 2; i <= n; i ++ )
{
int len = i - ne[i];
if(i % len == 0 && i != len) cout << i << ' ' << i / len << endl;
}
puts("");
}
return 0;
}
dfstrie
先转换异或结果的求法,转换为每个点到根节点的路径长度,然后用trie去查询能够异或出的最大值
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100010;
int n, idx = 0;
int h[N], e[N * 2], ne[N * 2], w[N * 2];
int dist[N], son[N * 32][2];
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++ ;
}
void dfs(int u, int fa, int k)
{
dist[u] = k;
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(j == fa) continue;
dfs(j, u, k ^ w[i]);
}
}
void insert(int x)
{
int p = 0;
for(int i = 31; i >= 0; i -- )
{
int r = x >> i & 1;
if(!son[p][r]) son[p][r] = ++ idx;
p = son[p][r];
}
}
int query(int x)
{
int res = 0, p = 0;
for(int i = 31; i >= 0; i -- )
{
int r = x >> i & 1;
if(son[p][!r])
{
p = son[p][!r];
res = res * 2 + !r;
}
else
{
p = son[p][r];
res = res * 2 + r;
}
}
return res;
}
int main()
{
memset(h, -1, sizeof h);
cin >> n;
for(int i = 1; i < n; i ++ )
{
int a, b, c; cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
dfs(0, -1, 0);
idx = 0;
for(int i = 0; i < n; i ++ ) insert(dist[i]);
int res = 0;
for(int i = 0; i < n; i ++ )
{
int t = query(dist[i]);
res = max(res, t ^ dist[i]);
}
cout << res;
return 0;
}
比较吊的kmp,也可以用hash做。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 200010;
int n, m, q;
char a[N], b[N];
int cnt[N], ne[N];
int main()
{
cin >> n >> m >> q;
cin >> a + 1 >> b + 1;
for(int i = 2, j = 0; i <= m; i ++ )
{
while(j != 0 && b[i] != b[j + 1]) j = ne[j];
if(b[i] == b[j + 1]) j ++ ;
ne[i] = j;
}
for(int i = 1, j = 0; i <= n; i ++ )
{
while(j != 0 && a[i] != b[j + 1]) j = ne[j];
if(a[i] == b[j + 1]) j ++ ;
cnt[j] ++ ;
}
for(int i = m; i >= 1; i -- ) cnt[ne[i]] += cnt[i];
while(q -- )
{
int x; cin >> x;
cout << cnt[x] - cnt[x + 1] << endl;
}
return 0;
}
两种做法!
堆:
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 10010;
typedef pair<int, int> PII;
PII a[N];
int main()
{
int n;
while(cin >> n)
{
for(int i = 0; i < n; i ++ ) cin >> a[i].second >> a[i].first;
sort(a, a + n);
priority_queue<int, vector<int>, greater<int> > q;
for(int i = 0; i < n; i ++ )
{
q.push(a[i].second);
if(a[i].first < q.size()) q.pop();
}
int res = 0;
while(q.size()) res += q.top(), q.pop();
cout << res << endl;
}
return 0;
}
再给一个并查集做法:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10010;
typedef pair<int, int> PII;
PII a[N];
int p[N], n;
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
int n;
while(cin >> n)
{
for(int i = 1; i < N; i ++ ) p[i] = i;
for(int i = 0; i < n; i ++ ) cin >> a[i].first >> a[i].second;
sort(a, a + n), reverse(a, a + n);
int res = 0;
for(int i = 0; i < n; i ++ )
{
int j = a[i].second;
int r = find(a[i].second);
if(r)
{
res += a[i].first;
p[r] = r - 1;
}
}
cout << res << endl;
}
return 0;
}
并查集
离线做法,并查集判断关系是否成立。
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
const int N = 200010;
unordered_map<int, int> s; int idx = 0;
typedef pair<int, int> PII;
vector<PII> f;
int p[N];
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
int T; cin >> T;
while(T -- )
{
int n; cin >> n;
idx = 0;
for(int i = 1; i <= 2 * n; i ++ ) p[i] = i;
f.clear(), s.clear();
for(int i = 0; i < n; i ++ )
{
int x, y, e; cin >> x >> y >> e;
if(!s.count(x)) s[x] = ++ idx;
if(!s.count(y)) s[y] = ++ idx;
x = s[x], y = s[y];
if(!e) f.push_back({x, y});
else
{
int a = find(x), b = find(y);
p[a] = b;
}
}
bool flag = true;
for(int i = 0; i < f.size(); i ++ )
{
int a = f[i].first, b = f[i].second;
if(find(a) == find(b))
{
flag = false;
break;
}
}
puts(flag ? "YES" : "NO");
}
return 0;
}
并查集和背包的运用
#include <iostream>
#include <cstring>
using namespace std;
const int N = 610, M = 1210;
int n, m, p1, p2;
int p[M], s[M], tot;
int vis[M], a[N], b[N];
int f[N][N];
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
void merge(int a, int b)
{
int x = find(a), y = find(b);
if(x == y) return;
p[x] = y, s[y] += s[x];
}
void init()
{
memset(f, 0, sizeof f);
m = p1 + p2, tot = 0; f[0][0] = 1;
for(int i = 1; i <= m; i ++ )
{
p[i] = i, p[i + m] = i + m;
s[i] = 1, s[i + m] = 0;
vis[i] = vis[i + m] = 0;
}
}
int main()
{
while(cin >> n >> p1 >> p2 && n + p1 + p2)
{
init();
while(n -- )
{
int a0, b0; string s;
cin >> a0 >> b0 >> s;
if(s == "yes") merge(a0, b0), merge(a0 + m, b0 + m);
else merge(a0 + m, b0), merge(a0, b0 + m);
}
for(int i = 1; i <= m; i ++ )
{
int j = find(i);
if(j != i) continue;
vis[j] = vis[j + m] = ++ tot;
a[tot] = s[j], b[tot] = s[j + m];
}
for(int i = 1; i <= tot; i ++ )
for(int j = min(a[i], b[i]); j <= p1; ++ j )
{
if(j >= a[i]) f[i][j] += f[i - 1][j - a[i]];
if(j >= b[i]) f[i][j] += f[i - 1][j - b[i]];
}
if(f[tot][p1] != 1) puts("no");
else
{
for(int i = tot; i; -- i )
if(f[i - 1][p1 - a[i]]) p1 -= a[i], a[i] = -1; // 以同类作为神
else if(f[i - 1][p1 - b[i]]) p1 -= b[i], a[i] = -2;
for(int i = 1; i <= m; i ++ )
{
int j = find(i);
if(j <= m && a[vis[j]] == -1) cout << i << endl;
else if(j > m && a[vis[j]] == -2) cout << i << endl;
}
puts("end");
}
}
return 0;
}
数状数组结合差分,解决区间修改单点查询
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 200010;
int a[N];
int c[N], n, m;
int lowbit(int x)
{
return x & -x;
}
int add(int x, int k)
{
for(int i = x; i <= n; i += lowbit(i)) c[i] += k;
}
int query(int x)
{
int sum = 0;
for(int i = x; i; i -= lowbit(i)) sum += c[i];
return sum;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
for(int i = 1; i <= n; i ++ ) add(i, a[i] - a[i - 1]);
while(m -- )
{
char op; cin >> op;
if(op == 'C')
{
int l, r, k; cin >> l >> r >> k;
add(l, k), add(r + 1, -k);
}
else
{
int k; cin >> k;
cout << query(k) << endl;
}
}
}
数状数组和二分综合
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int tr[N];
int c[N], n;
int h[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int c)
{
for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
int query(int x)
{
int sum = 0;
for(int i = x; i; i -= lowbit(i)) sum += tr[i];
return sum;
}
int main()
{
cin >> n;
for(int i = 2; i <= n; i ++ ) cin >> c[i];
for(int i = 1; i <= n; i ++ ) tr[i] = lowbit(i);
for(int i = n; i; i -- )
{
int l = 1, r = n;
while(l < r)
{
int mid = l + r >> 1;
if(query(mid) <= c[i]) l = mid + 1;
else r = mid;
}
add(r, -1);
h[i] = r;
}
for(int i = 1; i <= n; i ++ ) cout << h[i] << endl;
return 0;
}
数状数组,双向遍历。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 2e5+10;
typedef long long ll;
int n;
int h[N], c[N];
int larger[N], lower[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int k)
{
for(int i = x; i <= n; i += lowbit(i)) c[i] += k;
}
int sum(int x)
{
int res=0;
for(int i = x; i; i -= lowbit(i))
res += c[i];
return res;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++ ) cin >> h[i];
for(int i = 1; i <= n; i ++ ) // 从左到右
{
int y = h[i];
larger[i] = sum(n) - sum(y);
lower[i] = sum(y - 1);
add(y, 1);
}
memset(c, 0, sizeof c);
ll res1 = 0, res2 = 0;
for(int i = n; i >= 1; i -- ) // 反向遍历并计算
{
int y = h[i];
res1 += (ll) larger[i] * (sum(n) - sum(y));
res2 += (ll) lower[i] * sum(y - 1);
add(y, 1);
}
cout << res1 << ' ' << res2;
return 0;
}
线段树所有操作的练习!
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 4e5 + 10;
int n, p;
int w[N];
struct Node{
int l[N], r[N];
int v[N];
int tmul[N], tadd[N];
void pushup(int u)
{
v[u] = (v[u << 1] + v[u << 1 | 1]) % p;
}
void eval(int u, int add, int mul)
{
v[u] = ((ll)v[u] * mul + (ll)(r[u] - l[u] + 1) * add) % p;
tadd[u] = ((ll)tadd[u] * mul + add) % p;
tmul[u] = (ll)tmul[u] * mul % p;
}
void pushdown(int u)
{
eval(u << 1, tadd[u], tmul[u]);
eval(u << 1 | 1, tadd[u], tmul[u]);
tadd[u] = 0, tmul[u] = 1;
}
void build(int u, int a, int b)
{
if(a == b) l[u] = r[u] = a, v[u] = w[a], tmul[u] = 1;
else
{
l[u] = a, r[u] = b, v[u] = 0, tmul[u] = 1;
int mid = a + b >> 1;
build(u << 1, a, mid), build(u << 1 | 1, mid + 1, b);
pushup(u);
}
}
void modify(int u, int a, int b, int add, int mul)
{
if(a <= l[u] && r[u] <= b) eval(u, add, mul);
else
{
pushdown(u);
int mid = l[u] + r[u] >> 1;
if(a <= mid) modify(u << 1, a, b, add, mul);
if(b > mid) modify(u << 1 | 1, a, b, add, mul);
pushup(u);
}
}
int query(int u, int a, int b)
{
if(a <= l[u] && r[u] <= b) return v[u];
pushdown(u);
int mid = l[u] + r[u] >> 1;
int res = 0;
if(a <= mid) res = query(u << 1, a, b);
if(b > mid) res = (res + query(u << 1 | 1, a, b)) % p;
pushup(u);
return res;
}
}t;
int main()
{
cin >> n >> p;
for(int i = 1; i <= n; i ++ ) cin >> w[i];
t.build(1, 1, n);
int q; cin >> q;
while(q -- )
{
int op; cin >> op;
int a, b; cin >> a >> b;
if(op == 1)
{
int c; cin >> c;
t.modify(1, a, b, 0, c);
}
else if(op == 2)
{
int c; cin >> c;
t.modify(1, a, b, c, 1);
}
else
{
cout << t.query(1, a, b) << endl;
}
//for(int i = 1; i <= n; i ++ ) cout << t.query(1, i, i) << ' ';
//cout << endl;
}
return 0;
}
本文来自博客园,作者:{三季野花},转载请注明原文链接:https://www.cnblogs.com/SanGarden/articles/17069829.html