题目
一条数轴上有n个点,第i个点的坐标为i。
每个点有一个颜色
c
o
l
[
i
]
col[i]
col[i],任意相同颜色的点都可以用线将他们连起来,线段的颜色和点的颜色相同。
求有多少对线段相交,且他们颜色不同。
题解
直接搞不太好搞。正难则反,用总数减去不合法的即为答案。
这题的正解就是个暴力,但是设个阈值,分类讨论。
分两种情况:
情况①,AABB型。
利用
s
1
[
i
]
s_1[i]
s1[i]表示在
i
i
i前面有多少个相同颜色的点。
均摊
O
(
1
)
O(1)
O(1)搞一下即可。
时间复杂度:
O
(
n
)
O(n)
O(n)
情况②,ABBA型。
令出现次数
>
K
>K
>K的颜色为L,出现次数
≤
K
\leq K
≤K的颜色为P。
又有3种情况,
P
1
,
P
2
,
P
2
,
P
1
P_1,P_2,P_2,P_1
P1,P2,P2,P1,
∗
L
,
L
,
∗
*L,L,*
∗L,L,∗,
L
P
P
L
LPPL
LPPL。
情况一,
P
1
,
P
2
,
P
2
,
P
1
P_1,P_2,P_2,P_1
P1,P2,P2,P1,用树状数组求。
对于每个
P
1
(
i
,
j
)
P_1(i,j)
P1(i,j),统计有多少对
P
2
(
l
,
r
)
P_2(l,r)
P2(l,r),满足
i
<
l
<
r
<
j
i<l<r<j
i<l<r<j
对于每个r,将
O
(
k
)
O(k)
O(k)个l加入树状数组,然后求个前缀和即可。
求完之后,将i加入树状数组。
设置阈值之后,可知道L的种数不是很多,因此一个个枚举即可。
时间复杂度:
O
(
n
k
l
o
g
n
)
O(nk\ log\ n)
O(nk log n)
情况二,
∗
,
L
,
L
,
∗
*,L,L,*
∗,L,L,∗
对于每个
L
L
L,枚举每个
∗
*
∗,记第i个
∗
*
∗前面出现了
a
[
i
]
a[i]
a[i]个L。
则每个
(
i
,
j
)
(i,j)
(i,j),对答案的贡献为
C
a
i
−
a
j
2
C_{a_i-a_j}^2
Cai−aj2
维护
∑
j
=
1
i
−
1
a
[
j
]
2
\sum_{j=1}^{i-1}a[j]^2
∑j=1i−1a[j]2和
∑
j
=
1
i
−
1
a
[
j
]
\sum_{j=1}^{i-1}a[j]
∑j=1i−1a[j]即可。
时间复杂度:
O
(
n
2
k
)
O(\frac{n^2}{k})
O(kn2)
情况三,
L
P
P
L
LPPL
LPPL
对于每个
L
L
L,枚举每个P。只要知道每个P前面有多少个L,后面有多少个L即可。
时间复杂度:
O
(
n
2
k
)
O(\frac{n^2}{k})
O(kn2)
基于暴力扫,只不过不同情况不同方法罢了。
要说的
都到这个时候了,练练码力即可。
代码
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#define N 666670
#define mo 19260817
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int i,j,k,l,n,m,K;
int ans,sum,temp;
int gs[N],a[N],f1[N];
int pm[N];
int Sig_B[N],Bi,Ci;
int Sig_A[N],Sig_A2[N],Gs[N],Ai;
int tr[N];
int la[N],bw[N],hd[N];
int Calc[N];
bool bz[N];
int read(){
int fh=0,rs=0;char ch=0;
while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
if(ch=='-')fh=1,ch=getchar();
while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
return fh?-rs:rs;
}
int lowbit(int x){return x&(-x);}
void add(int x,int num){
for(;x<=n;x=x+lowbit(x))tr[x]=tr[x]+num;
}
int qry(int x){
int rs=0;
for(;x;x=x-lowbit(x))rs=rs+tr[x];
return rs;
}
int main(){
n=read();
fo(i,1,n){
a[i]=read();
if(!hd[a[i]])hd[a[i]]=i;
la[i]=bw[a[i]];
bw[a[i]]=i;
gs[a[i]]++;
}
fo(i,1,n)if(gs[i]){
f1[i]=1ll*gs[i]*(gs[i]-1)%mo;
f1[i]=1ll*f1[i]*9630409%mo;
sum=(sum+f1[i])%mo;
}
fo(i,1,n)if(gs[i]){
ans=(ans+1ll*f1[i]*(sum-f1[i]+mo)%mo)%mo;
}
ans=(1ll*ans*9630409)%mo;//总数
//AABB
pm[0]=0;
fo(i,2,n){
Calc[i]=(Calc[i-1]+i-1)%mo;
}
fo(i,1,n){
pm[i]=pm[la[i]]+1;
sum=(sum-(gs[a[i]]-pm[i])+mo)%mo;
temp=(sum-Calc[gs[a[i]]-pm[i]]+mo)%mo;
ans=(ans-1ll*temp*(pm[i]-1)%mo+mo)%mo;
}
//ABBA
K=sqrt(n/log2(n));
fo(i,1,n)if(gs[i]>=K)bz[i]=1;
//1 LPPL
fo(i,1,n)if(bz[i]){
fo(j,1,n)Sig_B[j]=0;
Bi=0;
fo(j,1,n){
k=a[j];
if(k==i)Bi++;
else
if(!bz[a[j]]){
Ci=gs[i]-Bi;
ans=(ans-1ll*Sig_B[k]*Ci%mo+mo)%mo;
Sig_B[k]+=Bi;
}
}
}
//2 *LL*
fo(i,1,n)if(bz[i]){
fo(j,1,n)Sig_A[j]=Sig_A2[j]=Gs[j]=0;
temp=0;
fo(j,1,n){
if(a[j]==i)Ai++;
else{
k=a[j];
temp=(temp+1ll*Gs[k]*(1ll*Ai*Ai)%mo)%mo;
temp=(temp+Sig_A2[k]+Sig_A[k])%mo;
temp=(temp-2ll*Ai*Sig_A[k]%mo)%mo;
temp=(temp-1ll*Gs[k]*Ai%mo)%mo;
Sig_A[k]=(Sig_A[k]+Ai)%mo;
Sig_A2[k]=(Sig_A2[k]+1ll*Ai*Ai%mo)%mo;
Gs[k]++;
}
}
ans=(ans-1ll*temp*9630409%mo+mo)%mo;
}
//3 PPPP
fo(i,1,n)if(gs[a[i]]&&!bz[a[i]]){
for(j=la[i];j;j=la[j]){
temp=(qry(i-1)-qry(j)+mo)%mo;
temp=(temp-Calc[pm[i]-pm[j]]+mo)%mo;
ans=(ans-temp+mo)%mo;
add(j,1);
}
}
printf("%d",ans);
return 0;
}

本文探讨了一道关于数轴上带有颜色的点和线段的复杂算法问题。目标是计算不同颜色线段的交叉数量。通过区分AABB和ABBA型线段,采用暴力解法结合优化策略,如树状数组和阈值分类,实现高效求解。文章详细阐述了解决方案的思路和具体实现步骤。
550

被折叠的 条评论
为什么被折叠?



