前置:cdq分治,( exttt{P4390}) (即cdq分治解决单点修改,矩阵查询的问题)。
此题便是 ( exttt{P4390}) 的巧妙转换。
题意
不做赘述。
但是为了更好地做题,题中:
数字 (X) 在 ([l,r]) 最后一次出现位置的下标减去第一次出现位置的下标。
等价于:
数字 (X) 在 ([l,r]) 中每次出现的下标相邻的差的绝对值。
思路
一个朴素的想法:
- 修改:用 (set) 动态维护维护每个位置的数与之相同的前面的数的位置,时间复杂度 (O(log N))。
- 查询暴力一个一个查 (sum_{i=l}^{ile r} i- exttt{pre}_i ( exttt{pre}_i ge l)) ,时间复杂度(O(N))。
考虑优化查询。
观察到每两个建立好的二元组 (( exttt{pre}_i,i)) 能对区间 ([l,r]) 产生贡献当且仅当:
- ( exttt{pre}_i ge l)
- (i le r)
- 以及操作的相对时间 (T),(change exttt T le query exttt T)
联系到 ( exttt{P4390}) ,这不就相当于是看成一个二维数对,点((x,y)) 就是上述二元组,查询的便是一个 ((l,l)) 和 ((r,r)) 构成的矩阵。
用cdq分治就可以解决了,cdq具体操作内容参考上题。(翻了翻 ( exttt{CF}) 上的记录或许还有其他做法,如莫队,扫描线...)。
注意修改操作一定要修改完整,当要删除一个二位数对的贡献时,加入与之相反贡献的相同数对即可。
代码
int a, b, s[N + 10], top, q[N + 10], ans[N + 10];
bool p[N + 10];
set<int> st[N + 10];
set<int>::iterator it;
struct node
{
int opt, t, x, y, val;
bool p;
} ask[N + 10];
inline bool cmp1(node &p1, node &p2)
{
return p1.x < p2.x || p1.x == p2.x && p1.opt < p2.opt;
}
inline bool cmp2(node &p1, node &p2)
{
return p1.t < p2.t;
}
inline void add(int n, int k)
{
for (; n <= a; n += n & -n)
q[n] += k;
}
inline int query(int n)
{
if (!n)
return 0;
int res = 0;
for (; n; n -= n & -n)
res += q[n];
return res;
}
inline void cdq(int l, int r)
{
if (l == r)
return;
int mid = (l + r) >> 1;
cdq(l, mid);
cdq(mid + 1, r);
for (int i = mid + 1; i <= r; i++)
ask[i].p = 1;
sort(ask + l, ask + r + 1, cmp2);
for (int i = l; i <= r; i++)
{
if (!ask[i].p)
{
if (ask[i].opt == 1)
add(ask[i].y, ask[i].val);
}
else
{
if (ask[i].opt == 2)
ans[ask[i].t] += query(ask[i].y) * ask[i].val;
}
}
for (int i = l; i <= r; i++)
{
if (!ask[i].p && ask[i].opt == 1)
add(ask[i].y, -ask[i].val);
ask[i].p = 0;
}
}
signed main()
{
// freopen("in1.in", "r", stdin);
a = read();
b = read();
for (int i = 1; i <= a; i++)
{
s[i] = read();
if (!st[s[i]].empty())
ask[++top] = (node){1, 0, *st[s[i]].rbegin(), i, i - *st[s[i]].rbegin()};
st[s[i]].insert(i);
}
int opt, x, y, z, w;
for (int i = 1; i <= b; i++)
{
opt = read();
x = read();
y = read();
if (opt == 1)
{
//delete
z = w = 0;
it = st[s[x]].find(x);
if (it != st[s[x]].begin())
{
--it;
ask[++top] = (node){1, i, *it, x, -(x - *it)};
z = *it;
++it;
}
++it;
if (it != st[s[x]].end())
ask[++top] = (node){1, i, x, *it, -(*it - x)}, w = *it;
if (z && w)
ask[++top] = (node){1, i, z, w, w - z};
st[s[x]].erase(x);
//insert
z = w = 0;
st[y].insert(x);
it = st[y].find(x);
if (it != st[y].begin())
{
--it;
ask[++top] = (node){1, i, *it, x, x - *it};
z = *it;
++it;
}
++it;
if (it != st[y].end())
ask[++top] = (node){1, i, x, *it, *it - x}, w = *it;
if (z && w)
ask[++top] = (node){1, i, z, w, z - w};
s[x] = y;
}
else
{
p[i] = 1;
ask[++top] = (node){2, i, x - 1, x - 1, 1};
ask[++top] = (node){2, i, x - 1, y, -1};
ask[++top] = (node){2, i, y, x - 1, -1};
ask[++top] = (node){2, i, y, y, 1};
}
}
sort(ask + 1, ask + top + 1, cmp1);
cdq(1, top);
for (int i = 1; i <= b; i++)
if (p[i])
printf("%lld
", ans[i]);
return 0;
}