反射弧
在\(x\)轴上面有\((1,0),(2,0),(3,0)...(n,0)\)共\(n\)个点,每个点有一种颜色,现在相同颜色的点之间都要连一条边,连边的画出来都是\(y>0\)平面上的一个半圆弧,求连边相交且颜色不同的弧对的数量(无序对)
数据范围:\(1\leq n \leq 10^5,1\leq c_i\leq n\),其中\(c_i\)表示第\(i\)个点的颜色
Solution
一道神秘分块题(逐渐失去分块能力qwq)
首先简化一下问题,我们要求的就是形如\(ABAB\)的数量,这个可以容斥一下,用总数\(-AABB-ABBA\)得到
总数比较好算,直接每种颜色选\(2\)个然后乘乘加加搞一下就好了
计算\(AABB\)可以从右往左扫一遍,维护右边的\(BB\)数量和左边\(A\)的数量
最后是\(ABBA\)
这里可以按照出现次数分块(其实好像也不是那种分块。。反正就是大的和小的分开处理)
设置一个阈值\(S\),那么出现次数大于\(S\)的颜色的特点是颜色种类少,小于\(S\)的颜色特点是出现次数少
如果\(A\)的出现次数\(>S\),那么我们从左往右扫一遍整个序列,对于每种非\(A\)颜色维护\(cntAB\),再维护一个\(cntA\)和\(totABB=\sum cntAB\),每次遇到一个\(A\)就将\(totABB\)累加到答案里面,遇到一个非\(A\)颜色就将\(cntA\)加到对应的\(cntAB\)里面去,这样就能将所有(\(A\)出现次数\(>S\)和\(B\)出现次数随意)的\(ABBA\)算进去了
如果\(A\)的出现次数\(<=S\),\(B\)的出现次数\(>S\),那么我们也可以在统计上一种情况的时候顺便算上,或者说就等价于遇到一个出现次数\(>S\)的\(A\)的时候统计\(ABBA\)和\(BAAB\),后者的\(B\)要求出现次数\(<=S\)
计算这个的话,假设我们现在遇到一个\(B\),考虑这个\(B\)的前面某个颜色相同的位置,这对\(B\)的贡献应该是\(\binom x 2\),其中\(x\)表示中间的\(A\)的数量,记为\(cntA-frontA\),那么有:
所以我们要对于每一个\(B\)要维护的就是\(\sum frontA\)和\(\sum frontA(frontA+1)\),以及一个\(cntB\)
最后是如果\(A\)和\(B\)的出现次数都\(<=S\)的情况,这种情况下因为出现次数少,那么弧的数量应该是\(O(\frac{n}{S}\cdot S^2)\)级别的,所以我们可以暴力将所有的弧列出来,然后转化成一个二维偏序问题,直接排序然后树状数组就可以解决了
总的复杂度是\(O(nSlogn+\frac{n^2}{S})\),在\(S=\sqrt{\frac{n}{logn}}\)时取到最优为\(O(n\sqrt{nlogn})\)
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#define ll long long
#define pb push_back
using namespace std;
const int N=1e5+10,MOD=1e9+7;
struct Pr{
int l,r,col;
Pr(int _l=0,int _r=0,int _col=0){l=_l; r=_r;col=_col;}
friend bool operator < (Pr x,Pr y){return x.l==y.l?x.r<y.r:x.l<y.l;}
}lis[N*200];
int col[N],sz[N];
int cnt[N],cnt1[N];
int cntA,cntAB[N],cntABB[N],totABB;
int cntB[N],frontA[N],frontA2[N];
vector<int> rec[N];
int n,inv2,tot,sq;
int ans;
int mns(int x,int y){return (1LL*x+MOD-y)-(1LL*x+MOD-y>=MOD?MOD:0);}
int plu(int x,int y){return (1LL*x+y)-(1LL*x+y>=MOD?MOD:0);}
int mul(int x,int y){return 1LL*x*y%MOD;}
int C2(int x){return x<2?0:mul(inv2,mul(x,x-1));}
int C4(int x){return x<4?0:1LL*x*(x-1)*(x-2)%MOD*(x-3)%MOD/24;}
int ksm(int x,int y){
int ret=1,base=x;
for (;y;y>>=1,base=mul(base,base))
if (y&1) ret=mul(ret,base);
return ret;
}
void prework(){
ans=0;
for (int c=1;c<=n;++c) sz[c]=rec[c].size();
int tmp=0;
for (int c=1;c<=n;++c){
ans=plu(ans,mul(C2(sz[c]),tmp));
tmp=plu(tmp,C2(sz[c]));
}
}
namespace Bit{
const int N=::N;
int c[N];
int mx;
void init(int _n){mx=_n;}
void modify(int x,int delta){for (;x;x-=x&-x) c[x]+=delta;}
int query(int x){
int ret=0;
for (;x<=mx;x+=x&-x) ret+=c[x];
return ret;
}
}
int AABB(){
int ret=0;
for (int c=1;c<=n;++c) cnt[c]=0,cnt1[c]=0;
for (int i=1;i<n;++i) ++cnt1[col[i]];
int tmp=0;
for (int i=n;i>1;--i){
tmp=mns(tmp,C2(cnt[col[i]]));
++cnt[col[i]];
tmp=plu(tmp,C2(cnt[col[i]]));
--cnt1[col[i-1]];
ret=plu(ret,mul(mns(tmp,C2(cnt[col[i-1]])),max(0,cnt1[col[i-1]])));
}
return ret;
}
void solve_big(int A){//ABBA || BAAB
for (int c=1;c<=n;++c)
cntB[c]=0,cntAB[c]=0,frontA[c]=0,frontA2[c]=0;
cntA=totABB=0;
int ret=0;
for (int i=1;i<=n;++i){
if (col[i]==A){
++cntA;
ret=plu(ret,totABB);
}
else{
totABB=plu(totABB,cntAB[col[i]]);
cntAB[col[i]]=plu(cntAB[col[i]],cntA);
if (sz[col[i]]<=sq){//BAAB
ret=plu(ret,mul(cntB[col[i]],C2(cntA)));
ret=mns(ret,mul(cntA,frontA[col[i]]));
ret=plu(ret,mul(frontA2[col[i]],inv2));
frontA[col[i]]=plu(frontA[col[i]],cntA);
frontA2[col[i]]=plu(frontA2[col[i]],mul(cntA,cntA+1));
}
++cntB[col[i]];
}
}
//printf("%d\n",ret);
ans=mns(ans,ret);
}
void solve_small(){
sort(lis+1,lis+1+tot);
Bit::init(n);
int p,c;
int ret=0;
for (int i=1;i<=tot;++i){
ret=plu(ret,Bit::query(lis[i].r+1));
Bit::modify(lis[i].r,1);
}
for (int c=1;c<=n;++c)
if (sz[c]<=sq) ret=mns(ret,C4(sz[c]));
ans=mns(ans,ret);
}
void solve(){
ans=mns(ans,AABB());
sq=max(1,(int)(sqrt(n/(log(1.0*n)/log(2.0)))));
for (int c=1;c<=n;++c){
if (!sz[c]) continue;
if (sz[c]>sq)
solve_big(c);
else{
for (int i=0;i<sz[c];++i)
for (int j=i+1;j<sz[c];++j)
lis[++tot]=Pr(rec[c][i],rec[c][j],c);
}
}
solve_small();
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",col+i),rec[col[i]].pb(i);
inv2=ksm(2,MOD-2);
prework();
solve();
printf("%d\n",ans);
}