反射弧

## Description

  在\(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\),那么有:

\[\begin{aligned} \binom {cntA-frontA}2&=\frac{(cntA-frontA)(cntA-1-frontA)}{2}\\ &=\frac{cntA(cntA-1)-cntA\cdot frontA -frontA(cntA-1)+frontA^2}{2}\\ &=\binom{cntA}{2}-cntA\cdot frontA-\frac{frontA(frontA+1)}{2} \end{aligned} \]

  所以我们要对于每一个\(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);
}
posted @ 2019-04-09 08:58  yoyoball  阅读(453)  评论(0)    收藏  举报