先离散化颜色。考虑对每种颜色单独求出答案。对于颜色 \(x\),可以用总方案数 \(n-k+1\) 减去一个 \(x\) 都不包含的区间数量。对于这个,假设相邻两个颜色 \(x\) 的下标分别为 \(l,r\),那么中间那段极长不含 \(x\) 的区间对答案的贡献就是 \(\max(0,r-l-k)\)。
看到区间推平,考虑 ODT 维护,区间推平可转化为若干加入或删除极长不含某种颜色的连续段的操作。对于一个固定的 \(l,r\),它对答案的贡献是一个等差数列的形式,即对 \(k=1\) 有 \(-(r-l-1)\) 贡献,对 \(k=2\) 有 \(-(r-l-2)\) 贡献,以此类推。用线段树维护,询问直接在线段树上查即可。
时间复杂度 \(O((n+q) \log n)\),带个 set
的常数。
code
// Problem: G. Growing flowers
// Contest: Codeforces - Bubble Cup 13 - Finals [Online Mirror, unrated, Div. 1]
// URL: https://codeforces.com/problemset/problem/1423/G
// Memory Limit: 256 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 long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 200100;
ll n, m, a[maxn];
namespace SGT {
ll tree[maxn << 2], tag[maxn << 2];
void pushup(int x) {
tree[x] = tree[x << 1] + tree[x << 1 | 1];
}
void pushdown(int x, int l, int r) {
if (!tag[x]) {
return;
}
int mid = (l + r) >> 1;
tree[x << 1] += tag[x] * (mid - l + 1);
tree[x << 1 | 1] += tag[x] * (r - mid);
tag[x << 1] += tag[x];
tag[x << 1 | 1] += tag[x];
tag[x] = 0;
}
void update(int rt, int l, int r, int ql, int qr, ll x) {
if (ql <= l && r <= qr) {
tree[rt] += x * (r - l + 1);
tag[rt] += x;
return;
}
pushdown(rt, l, r);
int mid = (l + r) >> 1;
if (ql <= mid) {
update(rt << 1, l, mid, ql, qr, x);
}
if (qr > mid) {
update(rt << 1 | 1, mid + 1, r, ql, qr, x);
}
pushup(rt);
}
ll query(int rt, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return tree[rt];
}
pushdown(rt, l, r);
int mid = (l + r) >> 1;
ll res = 0;
if (ql <= mid) {
res += query(rt << 1, l, mid, ql, qr);
}
if (qr > mid) {
res += query(rt << 1 | 1, mid + 1, r, ql, qr);
}
return res;
}
}
struct node {
ll l, r, x;
node(ll a = 0, ll b = 0, ll c = 0) : l(a), r(b), x(c) {}
};
set<node> st, ss[maxn];
bool operator < (node a, node b) {
return a.l < b.l;
}
struct que {
ll op, x, y, z;
} qq[maxn];
ll lsh[maxn], tot;
void add(ll x, ll y) {
if (x < 1) {
return;
}
SGT::update(1, 1, n, 1, 1, x * y);
SGT::update(1, 1, n, 2, min(n, x + 1), -y);
}
auto split(ll pos) {
auto it = st.lower_bound(node(pos));
if (it != st.end() && it->l == pos) {
return it;
}
--it;
node u = *it;
st.erase(it);
ss[u.x].erase(u);
ss[u.x].insert(node(u.l, pos - 1, u.x));
ss[u.x].insert(node(pos, u.r, u.x));
st.insert(node(u.l, pos - 1, u.x));
return st.insert(node(pos, u.r, u.x)).fst;
}
void insert(node u) {
ll l = (--ss[u.x].lower_bound(u))->r;
ll r = ss[u.x].upper_bound(u)->l;
add(r - l - 1, -1);
add(u.l - l - 1, 1);
add(r - u.r - 1, 1);
ss[u.x].insert(u);
}
void del(node u) {
ll l = (--ss[u.x].lower_bound(u))->r;
ll r = ss[u.x].upper_bound(u)->l;
add(r - l - 1, 1);
add(u.l - l - 1, -1);
add(r - u.r - 1, -1);
ss[u.x].erase(u);
}
void assign(ll l, ll r, ll x) {
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; ++it) {
del(*it);
}
st.erase(itl, itr);
st.insert(node(l, r, x));
insert(node(l, r, x));
}
int main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
lsh[++tot] = a[i];
}
for (int i = 1; i <= m; ++i) {
scanf("%lld%lld", &qq[i].op, &qq[i].x);
if (qq[i].op == 1) {
scanf("%lld%lld", &qq[i].y, &qq[i].z);
lsh[++tot] = qq[i].z;
}
}
sort(lsh + 1, lsh + tot + 1);
tot = unique(lsh + 1, lsh + tot + 1) - lsh - 1;
for (int i = 1; i <= tot; ++i) {
ss[i].insert(node(0, 0, i));
ss[i].insert(node(n + 1, n + 1, i));
add(n, 1);
}
st.insert(node(0, 0));
st.insert(node(n + 1, n + 1));
for (int i = 1; i <= n; ++i) {
a[i] = lower_bound(lsh + 1, lsh + tot + 1, a[i]) - lsh;
insert(node(i, i, a[i]));
st.insert(node(i, i, a[i]));
}
for (int i = 1; i <= m; ++i) {
if (qq[i].op == 1) {
qq[i].z = lower_bound(lsh + 1, lsh + tot + 1, qq[i].z) - lsh;
}
int op = qq[i].op;
if (op == 1) {
ll l = qq[i].x, r = qq[i].y, x = qq[i].z;
assign(l, r, x);
} else {
ll x = qq[i].x;
printf("%lld\n", tot * (n - x + 1) - SGT::query(1, 1, n, 1, x));
}
}
return 0;
}