洛谷 P7722 [Ynoi2007] tmpq

洛谷传送门

被踩爆了好神的题啊!

转化一下题意,给出三个数组 a,b,c,每次可以单点修改 a,b,c,询问即求 bi=aj=ck,1i<j<kr 的数量。

发现每种颜色是独立的,故先把询问离线下来,把颜色分开单独考虑。设 bi=aj=ck=w。考虑根号分治。设 B=n+m

  • 如果 w 的出现次数 B,直接暴力计算 dp 值,在 k 处加即可,查询直接查询前缀和,可以用分块平衡复杂度,O(1) 单点修改,O(B) 查询即可。
  • 如果 w 的出现次数 >B,这样的数不超过 B 个,对于每个数维护一个动态 dp,仍然是使用分块维护前缀矩阵积,O(B) 单点修改,O(1) 查询即可。

你会发现每一处都奇妙地平衡了时间复杂度。因此时间复杂度 O((n+m)n+m)

code
// Problem: P7722 [Ynoi2007] tmpq
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P7722
// Memory Limit: 64 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
/*
转化一下题意,给出三个数组 $a, b, c$,每次可以单点修改 $a, b, c$,询问即求 $b_i = a_j = c_k, 1 \le i < j < k \le r$ 的数量。
发现每种颜色是独立的,故先把询问离线下来,把颜色分开单独考虑。设 $b_i = a_j = c_k = w$。考虑根号分治。设 $B = \sqrt{n + m}$。
- 如果 $w$ 的出现次数 $\le B$,直接暴力计算 dp 值,在 $k$ 处加即可,查询直接查询前缀和,可以用分块平衡复杂度,$O(1)$ 单点修改,$O(B)$ 查询即可。
- 如果 $w$ 的出现次数 $> B$,这样的数不超过 $B$ 个,对于每个数维护一个动态 dp,仍然是使用分块维护前缀矩阵积,$O(B)$ 单点修改,$O(1)$ 查询即可。
你会发现每一处都奇妙地平衡了时间复杂度。因此时间复杂度 $O((n + m) \sqrt{n + m})$。
*/
const int maxn = 200100;
int n, m, B, a[maxn], b[maxn], c[maxn], bel[maxn], L[maxn], R[maxn], blo, oa[maxn];
ll ans[maxn];
vector<int> app[maxn];
vector<ll> lf[maxn];
struct wwh {
int op, x, y;
} qq[maxn];
// =================== small ===================
namespace BLS {
ll a[maxn], b[maxn];
inline void update(int x, ll y) {
a[x] += y;
b[bel[x]] += y;
}
inline ll query(int x) {
ll res = 0;
for (int i = 1; i < bel[x]; ++i) {
res += b[i];
}
for (int i = L[bel[x]]; i <= x; ++i) {
res += a[i];
}
return res;
}
}
inline void update(int t) {
if ((int)app[t].size() > B) {
return;
}
ll A = 0, B = 0, C = 0, lst = 0;
for (int i = 0; i < (int)app[t].size(); ++i) {
int k = app[t][i];
if (c[a[k]] == t) {
C += B;
}
if (a[k] == t) {
B += A;
}
if (b[a[k]] == t) {
++A;
}
BLS::update(k, C - lst - lf[t][i]);
lf[t][i] = C - lst;
lst = C;
}
}
inline void solve_small() {
for (int i = 1; i <= n; ++i) {
update(i);
}
for (int i = 1; i <= m; ++i) {
int op = qq[i].op, x = qq[i].x, y = qq[i].y;
if (op == 1) {
int t = a[x];
a[x] = y;
update(t);
update(b[t]);
update(c[t]);
update(y);
update(b[y]);
update(c[y]);
} else {
ans[i] += BLS::query(x);
}
}
}
// ==================== big ====================
struct node {
ll a, b, c, ab, bc, abc;
node(ll x = 0, ll y = 0, ll z = 0, ll u = 0, ll v = 0, ll w = 0) : a(x), b(y), c(z), ab(u), bc(v), abc(w) {}
} f[maxn];
inline node operator + (const node &a, const node &b) {
node res;
res.a = a.a + b.a;
res.b = a.b + b.b;
res.c = a.c + b.c;
res.ab = a.ab + b.ab + a.a * b.b;
res.bc = a.bc + b.bc + a.b * b.c;
res.abc = a.abc + b.abc + a.ab * b.c + a.a * b.bc;
return res;
}
namespace BLM {
node a[maxn], b[maxn];
inline void init() {
for (int i = 1; i <= bel[n]; ++i) {
a[L[i]] = f[L[i]];
for (int j = L[i] + 1; j <= R[i]; ++j) {
a[j] = a[j - 1] + f[j];
}
b[i] = b[i - 1] + a[R[i]];
}
}
inline void update(int x) {
a[L[x]] = f[L[x]];
for (int i = L[x] + 1; i <= R[x]; ++i) {
a[i] = a[i - 1] + f[i];
}
for (int i = 1; i <= bel[n]; ++i) {
b[i] = b[i - 1] + a[R[i]];
}
}
inline node query(int x) {
return b[bel[x] - 1] + a[x];
}
}
inline void solve_big() {
for (int k = 1; k <= n; ++k) {
if ((int)app[k].size() <= B) {
continue;
}
for (int i = 1; i <= n; ++i) {
f[i] = node();
a[i] = oa[i];
}
for (int x : app[k]) {
f[x] = node(b[a[x]] == k, a[x] == k, c[a[x]] == k);
}
BLM::init();
for (int i = 1; i <= m; ++i) {
int op = qq[i].op, x = qq[i].x, y = qq[i].y;
if (op == 1) {
if (b[a[x]] == k || a[x] == k || c[a[x]] == k || b[y] == k || y == k || c[y] == k) {
f[x] = node(b[y] == k, y == k, c[y] == k);
BLM::update(bel[x]);
}
a[x] = y;
} else {
ans[i] += BLM::query(x).abc;
}
}
}
}
void solve() {
scanf("%d%d", &n, &m);
B = sqrt(n + m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
oa[i] = a[i];
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &b[i]);
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &c[i]);
}
for (int i = 1; i <= n; ++i) {
app[a[i]].pb(i);
app[b[a[i]]].pb(i);
app[c[a[i]]].pb(i);
}
blo = sqrt(n);
for (int i = 1; i <= n; ++i) {
bel[i] = (i - 1) / blo + 1;
if (!L[bel[i]]) {
L[bel[i]] = i;
}
R[bel[i]] = i;
}
for (int i = 1; i <= m; ++i) {
scanf("%d%d", &qq[i].op, &qq[i].x);
if (qq[i].op == 1) {
scanf("%d", &qq[i].y);
app[qq[i].y].pb(qq[i].x);
app[b[qq[i].y]].pb(qq[i].x);
app[c[qq[i].y]].pb(qq[i].x);
}
}
for (int i = 1; i <= n; ++i) {
sort(app[i].begin(), app[i].end());
app[i].resize(unique(app[i].begin(), app[i].end()) - app[i].begin());
lf[i].resize(app[i].size());
}
solve_small();
solve_big();
for (int i = 1; i <= m; ++i) {
if (qq[i].op == 2) {
printf("%lld\n", ans[i]);
}
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}
posted @   zltzlt  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示