描述
YJC最近在研究数组,他认为如果数组的一个区间内不包含重复的元素,那么这个区间是一个优美的区间。
现在YJC弄到了一个interesting的数组,他想知道这个数组有多少个优美的区间。当然,他有时候也会觉得这个数组不够interesting,此时他会修改数组中的一个元素。
YJC发现在修改之后他不会算有多少个优美的区间了,于是他来向你求助。
输入格式
第一行输入一个数n,表示数组长度。
接下来一行输入n个整数,第i个整数ai是数组中的第ii个元素。
接下来一行输入一个数q,表示操作数量。
接下来q行每行表示一次操作。如果某行第一个数为0,则表示一次询问。否则后面输入两个数x和y,表示将a[x]修改为y。
输出格式
对于每次询问,输出一行一个整数表示答案。
样例1
样例输入
3
1 2 3
3
0
1 1 2
0
样例输出
6
4
和标算无关的暴力就不说了
我们考虑一个正确性显然的事情
我们记
next[i]
n
e
x
t
[
i
]
为
a[i]
a
[
i
]
左边第一个相等的位置
那么对于左端点
i
i
来说,他可能的右端点最大值为{
next[j]
n
e
x
t
[
j
]
}
(j>=i)
(
j
>=
i
)
那么我们就得到一个
O(nm)
O
(
n
m
)
的暴力。
考虑怎么优化
这个
next
n
e
x
t
数组的更新可以通过
set
s
e
t
做到
logn
l
o
g
n
,那么现在另一个瓶颈就是答案的计算
考虑用线段树维护。
维护一个类似区间和的东西(实际上不是区间和)
我们是用线段树维护一下每个点的
next
n
e
x
t
以及一下几个变量:
mn[root]
m
n
[
r
o
o
t
]
,区间内
next
n
e
x
t
的最小值
sum[root]
s
u
m
[
r
o
o
t
]
,区间内除了最小值左边的位以外每一位的
min
m
i
n
{
next[j]
n
e
x
t
[
j
]
}
(j>=i)
(
j
>=
i
)
det[root]
d
e
t
[
r
o
o
t
]
,维护
sum[root]−sum[ls]−sum[rs]
s
u
m
[
r
o
o
t
]
−
s
u
m
[
l
s
]
−
s
u
m
[
r
s
]
这些变量在我们转移以及计算答案时都要用到,缺一不可
具体转移见代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
#define ls root * 2
#define rs root * 2 + 1
const int maxn = 101000;
int n , m;
int a[maxn];
int nextt[maxn] , last[maxn];
ll mn[4*maxn] , delt[4*maxn] , sum[4*maxn];
ll p[4*maxn];
ll pl , tmp , pos_mn;
set<int>in[maxn];
set<int>::iterator it;
int read()
{
int sum = 0;char c = getchar();bool flag = true;
while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
if(flag) return sum;
else return -sum;
}
void find(int root,int l,int r)
{
int mid = (l + r)/2;
if(l == r)
{
pl = l;
return;
}
if(p[mn[rs]] < pos_mn) find(rs,mid+1,r);
else
{
tmp += sum[rs] + delt[root];
find(ls,l,mid);
}
return;
}
void update(int root,int l,int r)
{
if(p[mn[rs]] < p[mn[ls]])
{
mn[root] = mn[rs];
sum[root] = sum[rs];
return;
}
int mid = (l+r)/2;
mn[root] = mn[ls];
tmp = pl = 0;
pos_mn = p[mn[rs]];
find(ls,l,mid);
sum[root] = sum[ls] + sum[rs] - tmp + (p[mn[rs]] * (mn[rs] - pl) );
delt[root] = sum[root] - sum[ls] - sum[rs];
return;
}
void build(int root,int l,int r)
{
if(l == r)
{
mn[root] = l;sum[root] = 0;
return;
}
int mid = (l + r)/2;
build(ls,l,mid);build(rs,mid+1,r);
update(root , l , r);
}
void init()
{
n = read();
rep(i,1,n)
{
a[i] = read();
in[a[i]].insert(i);
last[i] = p[a[i]];
if(last[i]) nextt[last[i]] = i;
p[a[i]] = i;
}
rep(i,1,n)
{
if(!nextt[i]) nextt[i] = n + 1;
p[i] = nextt[i];
}
build(1,1,n);
return;
}
void New(int root,int l,int r,int pl)
{
if(l == r) return;
int mid = (l + r)/2;
if(mid >= pl) New(ls,l,mid,pl);
else New(rs,mid+1,r,pl);
update(root,l,r);
return;
}
void change(int x,int v)
{
p[x] = v;
New(1,1,n,x);
return;
}
int main()
{
init();
m = read();
rep(i,1,m)
{
int k = read();
if(k == 0)
printf("%lld\n",sum[1] + p[mn[1]] * mn[1] - 1ll*n*(n+1)/2);
else
{
int pl = read() , x = read();
if(last[pl]) nextt[last[pl]] = nextt[pl],
change(last[pl],nextt[pl]);
change(pl,n+1);
if(nextt[pl]) last[nextt[pl]] = last[pl];
in[a[pl]].erase(pl);
a[pl] = x;it = in[a[pl]].lower_bound(pl);
if(it == in[a[pl]].end()) nextt[pl] = n + 1;
else nextt[pl] = *it,last[nextt[pl]] = pl;
if(it == in[a[pl]].begin()) last[pl] = 0;
else last[pl] = *(--it),nextt[last[pl]] = pl;
in[a[pl]].insert(pl);
change(pl,nextt[pl]);
if(last[pl]) change(last[pl],pl);
}
}
return 0;
}