ABC318E Sandwiches
第一次场切 E 题,感动。虽然比较水
注意到 \(\{a_n\}\) 的值域上限为 \(n\),考虑值域相关算法,对每一个 \(a_i\) 开一个 std::vector
,记作 \(pos_{a_i}\),存储 \(a_i\) 所有的出现位置。
枚举 \(x \in [1,n]\),遍历 \(pos_x\),当遍历到第 \(i\) 个时,枚举 \(j \in [1,i)\),此时需要统计二元组 \((j,i)\) 的贡献,即有多少个 \(k\in (i,j)\) 使得三元组 \((j,k,i)\) 符合要求。
容易发现这个贡献就是 \((pos_{x,i}-pos_{x,j}-1)-(i-j-1)\)。\((pos_{x,i}-pos_{x,j}-1)\) 表示区间 \((i,j)\) 内有多少数,原因显然。\((i-j-1)\) 表示区间 \((i,j)\) 内有多少 \(a_i\),因为 \(pos_x\) 只存了 \(x\) 的出现位置,所以 \(\forall k \in (i,j),a_{pos_{x,k}}=a_i=a_j\)。
至此有了一个总复杂度 \(\mathcal{O}(n^2)\) 的做法,代码:
const int N=3e5+8;
int n,a[N];
vector<int> pos[N];
bool Mend;
signed main(){
// File_Work();
fprintf(stderr,"%.3lf MB\n\n\n",(&Mbegin-&Mend)/1048576.0);
n=read();
for(int i=1;i<=n;i++)
pos[i].push_back(0);
for(int i=1;i<=n;i++){
a[i]=read();
pos[a[i]].push_back(i);
}
int ans=0;
for(int x=1;x<=n;x++){
if((int)pos[x].size()==1)
continue;
for(int i=1;i<(int)pos[x].size();i++)
for(int j=1;j<i;j++)
ans+=(pos[x][i]-pos[x][j]-1-(i-j-1));
}
write(ans);
fprintf(stderr,"\n\n\n%.0lf ms",1e3*clock()/CLOCKS_PER_SEC);
return 0;
}
考虑优化,容易发现瓶颈是枚举 \(j \in [1,i)\)。
化简式子,二元组 \((j,i)\) 的贡献为 \(pos_{x,i}-pos_{x,j}-i+j\)。
考虑 拆贡献,\(pos_{x,i}\) 对答案产生了 \(i-1\) 次的正贡献,而 \(i\) 对答案产生了 \(i-1\) 次的负贡献。
对于与 \(j\) 有关的项,它们是以前缀和的方式贡献答案。具体地,每枚举到一个 \(i\),\(pos_{x,j}\) 对答案产生 \(\sum_{j=1}^{i-1}sum_{pos,j}\) 的负贡献,而 \(j\) 对答案产生 \(\sum_{j=1}^{i-1}j=\dfrac{i \times (i-1)}{2}\) 的正贡献。
因此预处理 \(pos_x\) 的前缀和,可以 \(\mathcal{O}(1)\) 计算出每个 \(i\) 对答案的贡献,故总复杂度 \(\mathcal{O}(n)\)。
代码:
const int N=3e5+8;
int n,a[N];
vector<int> pos[N];
int sum[N];
bool Mend;
signed main(){
// File_Work();
fprintf(stderr,"%.3lf MB\n\n\n",(&Mbegin-&Mend)/1048576.0);
n=read();
for(int i=1;i<=n;i++)
pos[i].push_back(0);
for(int i=1;i<=n;i++){
a[i]=read();
pos[a[i]].push_back(i);
}
int ans=0;
for(int x=1;x<=n;x++){
if((int)pos[x].size()==1)
continue;
int sz=(int)pos[x].size();
for(int i=1;i<sz;i++)
sum[i]=sum[i-1]+pos[x][i];
for(int i=1;i<sz;i++)
ans+=pos[x][i]*(i-1)-sum[i-1]-i*(i-1)+i*(i-1)/2;
}
write(ans);
fprintf(stderr,"\n\n\n%.0lf ms",1e3*clock()/CLOCKS_PER_SEC);
return 0;
}