P7708 题解
前言
莫队好题,比较板但又比较考转移的思维。
思路
很简单的发现是对操作序列进行莫队。
先对问题进行转换。
首先对于两种修改,第一种修改显然是没有第二种好写的,考虑将第一种转换为第二种。想到对于每一次第一种修改可以单开一个点,将其值赋值为要修改的值,这样就可以将第一种修改转换为第二种修改了,即将赋值转换为这个点与新开的点交换点值,这样方便撤销修改。
接下来就应该考虑 add() 与 del() 的写法了。
删除和扩展到的如果是查询点只有加与减的差别。如果是修改点,由于修改的操作是交换,换两次相当于没换,删除和扩展的操作应是一样的。
对于向右扩展与删除,是不会影响到前面的修改的,但对于向左的扩展与删除显然是会的,所以需要分开写。我们假设我们不直接在 \(a\) 数组上修改,而是在 \(rfl\) 数组上修改,\(rfl_i\) 表示进行一系列的交换后 \(i\) 位置所对应的原来的在 \(a\) 数组上的位置,再开两个数组分别为 \(pos\) 与 \(cnt\),\(pos_i\) 表示原来 \(a\) 数组上的 \(i\) 位置现在在 \(rfl\) 数组上对应的位置下标,\(cnt_i\) 表示原来的 \(a\) 数组中 \(i\) 的位置在答案中加了几次。
先考虑更简单的向右扩展或删除。
向右扩展显然是在 \(rfl\) 数组 上进行修改。如果扩展到的是查询点,直接修改答案与 \(cnt\) 数组即可。如果扩展到的是修改点,假设交换的位置为 \(u\) 和 \(v\),直接交换 \(rfl\) 数组上对应位置的值,并且修改 \(pos\) 数组上对应位置的值,即交换 \(rfl_u\) 和 \(rfl_v\) 的值,同时交换 \(pos_{rfl_u}\) 和 \(pos_{rfl_v}\) 的值。
再考虑向左扩展或删除。
显然向左扩展我们应该在 \(a\) 数组 上进行操作,因为向左扩展或删除的操作显然是最先进行的。如果是查询点方法与向右类似,不再多说。如果是修改点,首先应该减去 \(u\) 和 \(v\) 两点对答案的贡献。显然直接交换 \(a\) 的值是不行的,不然多次交换后会出问题。所以我们应该交换其在 \(rfl\) 对应位置上的值,并交换 \(cnt\) 数组上的值。毕竟如果最开始就将两个位置的值交换了,那后续对这两个位置的查询次数显然也是要交换的。总结下来就是要交换 \(pos_u\) 与 \(pos_v\) 的值,交换 \(rfl_{pos_u}\) 与 \(rfl_{pos_v}\) 的值,交换 \(cnt_u\) 与 \(cnt_v\) 的值,最后别忘了把两个位置的贡献加上。
然后外面套个莫队板子就做完了。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define usd unsigned
#define el cout << '\n'
#define lowbit(x) (x & (-x))
const int ranmod = 1e7;
#define random ((rand() * rand()) % ranmod)
#define AC return
#define AK return 0
#define YS cout << "YES"
#define NO cout << "NO"
#define Ys cout << "Yes"
#define No cout << "No"
#define ys cout << "yes"
#define no cout << "no"
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define debug(num) cerr << #num << ' ' << num << '\n'
// void init();
void main_();
signed main() {
ios :: sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
int t = 1;
// cin >> t;
while(t--) {
// init();
main_();
}
AK;
}
const int maxn = 2 * 1e5 + 6;
int n, block, m, a[maxn * 2], tot, ans[maxn], q, pos[maxn * 2], cnt[maxn * 2], rfl[maxn * 2];
struct node {
int l, r, id;
node(int ll = 0, int rr = 0, int idt = 0) { l = ll, r = rr, id = idt; }
} ;
vector <node> ask;
bool cmp(node a, node b) {
if(a.l / block != b.l / block) return a.l < b.l;
else return a.r < b.r;
}
struct node1 {
int op, u, v;
} b[maxn];
usd answer = 0;
void addl(int i) {
if(b[i].op == 0) { int x = b[i].u; answer += a[x], cnt[x]++; AC; }
answer -= a[b[i].u] * cnt[b[i].u];
answer -= a[b[i].v] * cnt[b[i].v];
swap(cnt[b[i].v], cnt[b[i].u]);
swap(pos[b[i].u], pos[b[i].v]);
swap(rfl[pos[b[i].u]], rfl[pos[b[i].v]]);
answer += a[b[i].u] * cnt[b[i].u];
answer += a[b[i].v] * cnt[b[i].v];
}
void dell(int i) {
if(b[i].op == 0) { int x = b[i].u; answer -= a[x], cnt[x]--; AC; }
answer -= a[b[i].u] * cnt[b[i].u];
answer -= a[b[i].v] * cnt[b[i].v];
swap(cnt[b[i].v], cnt[b[i].u]);
swap(pos[b[i].u], pos[b[i].v]);
swap(rfl[pos[b[i].u]], rfl[pos[b[i].v]]);
answer += a[b[i].u] * cnt[b[i].u];
answer += a[b[i].v] * cnt[b[i].v];
}
void addr(int i) {
if(b[i].op == 0) { int x = rfl[b[i].u]; answer += a[x], cnt[x]++; AC; }
swap(rfl[b[i].u], rfl[b[i].v]);
swap(pos[rfl[b[i].u]], pos[rfl[b[i].v]]);
}
void delr(int i) {
if(b[i].op == 0) { int x = rfl[b[i].u]; answer -= a[x], cnt[x]--; AC; }
swap(rfl[b[i].u], rfl[b[i].v]);
swap(pos[rfl[b[i].u]], pos[rfl[b[i].v]]);
}
void main_() {
cin >> n >> m;
block = sqrt(m);
for(int i = 1; i <= n; i++) cin >> a[i];
tot = n;
for(int i = 1; i <= m; i++) {
cin >> b[i].op >> b[i].u;
if(b[i].op == 3) { b[i].op = 0, b[i].v = b[i].u; continue; }
cin >> b[i].v;
if(b[i].op == 1) { a[++tot] = b[i].v; b[i].v = tot; }
else b[i].op = 1;
}
for(int i = 1; i <= tot; i++) cnt[i] = 0, pos[i] = rfl[i] = i;
cin >> q;
for(int i = 1, l, r; i <= q; i++) {
cin >> l >> r;
ask.push_back(node(l, r, i));
}
sort(ask.begin(), ask.end(), cmp);
int l = 1, r = 0;
for(auto tmp : ask) {
int ll = tmp.l, rr = tmp.r, id = tmp.id;
while(l > ll) l--, addl(l);
while(r < rr) r++, addr(r);
while(l < ll) dell(l), l++;
while(r > rr) delr(r), r--;
ans[id] = answer;
}
for(int i = 1; i <= q; i++) cout << ans[i] << '\n';
}

浙公网安备 33010602011771号