题意:
数据范围:
真—
100
100
100%:
N
<
=
2
∗
1
0
5
N<=2*10^5
N<=2∗105
时限3.5S,开O2。
Analysis:
很容易想到怎么处理相交,相包含的情况,但是如果要去掉相包含,则显得非常困难。
如果用数据结构维护,却找不到一个策略去优化,这时候不妨套路地想想平衡规划。
我们设一个阈值
K
K
K,出现次数大于
K
K
K的颜色为集合
Q
Q
Q,出现次数小于等于
K
K
K的颜色为集合
P
P
P。
显然
Q
Q
Q大小不会超过
N
K
\frac{N}{K}
KN。进一步分析:
首先我们可以算出不同颜色对的圆的总数,然后减去不合法的。
第一种情况:不相交的圆。
我们可以枚举圆的右端点,然后算出有多少不同颜色的圆在其右边,注意减去相同颜色。这个可以
O
(
N
)
O(N)
O(N)。
第二种情况:相含
根据颜色所属集合划分情况:
1.
Q
.
.
P
.
.
P
.
.
Q
Q..P..P..Q
Q..P..P..Q
Q
Q
Q个数少,我们可以去枚举
Q
Q
Q,然后对于每一个
Q
Q
Q做。对于第
i
i
i个
P
P
P,设它前面的当前
Q
Q
Q个数为
p
r
e
i
pre_i
prei,后面的当前
Q
Q
Q为
s
u
f
i
suf_i
sufi,那么它的答案会是:
∑
j
=
1
i
−
1
p
r
e
i
∗
s
u
f
i
\sum_{j=1}^{i-1}pre_i*suf_i
∑j=1i−1prei∗sufi。复杂度
O
(
N
2
K
)
O(\frac{N^2}{K})
O(KN2)。
2.
∗
.
.
Q
.
.
Q
.
.
∗
*..Q..Q..*
∗..Q..Q..∗
表示中间为
Q
Q
Q,两边为任何颜色的答案。
一样的,
Q
Q
Q个数少,我们枚举每一个
Q
Q
Q,然后统计答案。若当前颜色出现了第
i
i
i次,设
s
i
s_i
si表示当前
Q
Q
Q在
1
i
1~i
1 i中出现了
s
i
s_i
si次。那么答案为:
∑
j
=
1
i
−
1
(
s
i
−
s
j
)
∗
(
s
i
−
s
j
−
1
)
2
\sum_{j=1}^{i-1}\frac{(s_i-s_j)*(s_i-s_j-1)}{2}
∑j=1i−12(si−sj)∗(si−sj−1)。把式子拆开之后,发现我们只需要维护一个
s
s
s的前缀和,和
s
s
s的平方前缀和即可。复杂度同上一种情况。
3.
P
.
.
P
.
.
P
.
.
P
P..P..P..P
P..P..P..P
发现
P
P
P出现次数比较少,我们可以对于每一位,枚举前面的同颜色
P
P
P算答案。
我们记一个位置的贡献为,以其为左端点有多少相同颜色的圆。那么每一次枚举前面的相同颜色
P
P
P,用树状数组查询一下这个区间内有多少对圆,减掉和当前颜色相同的对数,并在其位置上加
1
1
1。复杂度
O
(
N
K
log
N
)
O(NK\log{N})
O(NKlogN)。
K
K
K取多少时优呢,解一下等式即可得到,当
K
K
K取
N
log
N
\sqrt{\frac{N}{\log{N}}}
logNN时最优。
总复杂度:
O
(
N
N
log
N
)
O(N\sqrt{N\log{N}})
O(NNlogN)。
能过了这个题也是比较玄学。
Code:
# include<cstdio>
# include<cstring>
# include<algorithm>
# include<cmath>
# include<vector>
using namespace std;
# define pb push_back
const int N = 7e5 + 5;
const int mo = 19260817;
const int inv = mo - mo / 2;
typedef long long ll;
vector <int> pos[N];
int a[N],Q[N],t[N],vis[N];
int s[N],s1[N],num[N],las[N];
int n,ans,h;
inline int inc(int x,int y)
{ return x + y >= mo ? x + y - mo : x + y; }
inline int dec(int x,int y)
{ return x - y < 0 ? x - y + mo : x - y; }
inline void add(int x)
{ for (int i = x ; i <= n ; i += i & (-i)) ++t[i]; }
inline int qry(int x)
{
int ret = 0;
for (int i = x ; i ; i -= i & (-i)) ret = inc(ret,t[i]);
return ret;
}
int main()
{
freopen("everytime.in","r",stdin);
freopen("everytime.out","w",stdout);
scanf("%d",&n); int lim = sqrt(n / min(n,15));
for (int i = 1 ; i <= n ; ++i) scanf("%d",&a[i]),++t[a[i]];
for (int i = 1 ; i <= n ; ++i)
if (t[i] > lim) Q[++h] = i,vis[i] = 1;
memset(t,0,sizeof(t));
for (int i = n ; i ; --i) num[i] = inc(num[i + 1],t[a[i]]),++t[a[i]];
int sum = 0;
for (int i = 1 ; i <= n ; ++i)
{
int c = (ll)t[i] * (t[i] - 1) / 2 % mo;
ans = inc(ans,(ll)c * sum % mo),sum = inc(sum,c);
}
for (int i = 1 ; i <= n ; ++i)
--t[a[i]],ans = dec(ans,(ll)s[a[i]] * dec(num[i + 1],(ll)t[a[i]] * (t[a[i]] - 1) / 2 % mo) % mo),++s[a[i]];
memset(t,0,sizeof(t));
for (int i = 1 ; i <= h ; ++i)
{
int now = 0;
for (int j = n ; j ; --j) if (a[j] == Q[i]) s[j] = s[j + 1] + 1; else s[j] = s[j + 1];
for (int j = 1 ; j <= n ; ++j)
if (!vis[a[j]])
{
ans = dec(ans,(ll)num[las[a[j]]] * s[j + 1] % mo);
num[j] = inc(num[las[a[j]]],now),las[a[j]] = j;
}else if (a[j] == Q[i]) ++now;
memset(las,0,sizeof(las));
}
for (int i = 1 ; i <= h ; ++i)
{
memset(s,0,sizeof(s)),memset(s1,0,sizeof(s1)),memset(num,0,sizeof(num));
int now = 0;
for (int j = 1 ; j <= n ; ++j)
if (a[j] != Q[i])
{
int c = dec(inc((ll)num[a[j]] * now * now % mo,s1[a[j]]),inc((ll)num[a[j]] * now % mo,2ll * now * s[a[j]] % mo));
c = (ll)inc(c,s[a[j]]) * inv % mo;
ans = dec(ans,c),++num[a[j]],s[a[j]] = inc(s[a[j]],now),s1[a[j]] = inc(s1[a[j]],(ll)now * now % mo);
}else ++now;
}
for (int i = 1 ; i <= n ; ++i)
if (!vis[a[i]])
{
int all = pos[a[i]].size();
for (int j = 0 ; j < all ; ++j)
{
int c = dec(dec(qry(i),qry(pos[a[i]][j] - 1)),(ll)(all - j) * (all - j - 1) / 2 % mo);
ans = dec(ans,c),add(pos[a[i]][j]);
}
pos[a[i]].pb(i);
} printf("%d\n",ans);
return 0;
}