海鸟夏季霸飞(P17833 migrate 大学习。)

设 \(pre_i\) 表示 \(i\) 往前跳 \(1\) 步最远跳到哪里,\(nxt_i\) 表示 \(i\) 往后跳 \(1\) 步最远跳到哪里。

考虑两个岛屿满足条件时它们的路径是怎样的。

根据样例的启发,设两个点为 \(x,y\),则两个路径形如:\(x \to (一段相同位置) \to u \to p-1 / p \to v \to (一段相同位置) \to y\)。即中间不同的点只会差 \(1\)。

考虑对 \(u \to p-1 / p \to v\) 这个东西计数之后再考虑左右两侧能接上哪些路径。这里“接上”的意思是,从其他点到 \(u\),或者从 \(v\) 到其他点,均有且只有一条最短路径,且这条最短路径经过 \(u/v\)。

首先考虑对于一条路径中的 \(u\),\(x \to u\) 的 \(x\) 的数量。枚举 \(i\) 作为 \(u\),考虑有哪些 \(j \le i\) 可以接上。\(i\) 自己自然可以。对于 \([pre_i+1,i-1]\) 中的 \(j\),如果 \(nxt_j>i\),说明我接不上 \(i\)。

对于 \(\le pre_i\) 的 \(j\),一种跳 \(j \to i\) 的最短路径的方式是,每次都跳 \(\min(pre_{now},i)\),这样肯定是最优的。\(i \to j\) 同理跳 \(nxt\)。那么有一个 \(O(n^3)\) 的方法就是,直接暴力跳 \(i \to j\),再暴力 \(j \to i\),如果两条路径不一样,说明至少有两条不同路径(边是双向),所以接不上。

考虑优化,设 \(f_i\) 表示有多少个 \(j \le i\) 满足条件。则贡献为:

  • \(i\) 自己,贡献为 \(1\)。
  • \([pre_i+1,i-1]\) 的点,用上述的方法判断即 \(\displaystyle \sum_{j=pre_i+1}^{i-1} [nxt_j=i]\)。
  • \(\le pre_i\) 的点,考虑直接把 \(pre_i\) 和 \(i\) 接起来,接起来的条件和上面的一样,即贡献 \([nxt_{pre_i}=i]f_{pre_i}\)。

即 \(\displaystyle f_i=1+\sum_{j=pre_i+1}^{i-1} [nxt_j=i]+[nxt_{pre_i}=i]f_{pre_i}\)。

考虑 \(v \to y\),枚举 \(i\) 作为 \(v\)。同理,设 \(g_i\) 表示有多少个 \(j \ge i\) 满足条件。

则 \(\displaystyle g_i=1+\sum_{j=i+1}^{nxt_i-1} [pre_j=i]+[pre_{nxt_i}=i]g_{nxt_i}\)。

中间的那一块可以用双指针维护。

现在对于 \(u \to p-1/p \to v\) 统计一下贡献。

注意到若希望 \(u \to v\) 存在且只存在这两条路径,应该有 \(p=nxt_u,p-1=pre_v\) 即 \(nxt_u-1=pre_v\),否则中转点会更多或更少。由于是两步从 \(u \to v\),所以会分出四种情况。

  • \(nxt_{nxt_u}=v,pre_{pre_v}=u\)。则两边都能接上路径,贡献为 \(f_ug_v\)。
  • \(nxt_{nxt_u}=v,pre_{pre_v}<u\)。如图,左边不能接上,因为左边可以直接跳到 \(pre_v\) 再跳到 \(v\),而不是跳到 \(u\) 再跳两步,则贡献为 \(g_v\)。
  • \(nxt_{nxt_u}>v,pre_{pre_v}=u\)。同上,右边不能接上,则贡献为 \(f_u\)。
  • \(nxt_{nxt_u}>v,pre_{pre_v}<u\)。左右两边都不能接上,贡献为 \(1\)。

对于第一种和第二种情况,可以枚举 \(u\),则 \(v=nxt_{nxt_u}\),然后考虑 \(v\) 满足的条件并计算贡献;对于第三种同理可以枚举 \(v\)。

对于第四种,枚举 \(u\),把 \(v\) 满足的条件列一下,即 \(nxt_u < v <nxt_{nxt_u},pre_{pre_v}<u,pre_v=nxt_u-1\)。前两个条件可以用双指针维护 \(v\),然后开个桶,每次查到一个 \(v\) 就修改 \(pre_v\) 处的权值,贡献就是桶中 \(nxt_u-1\) 的权值。

把以上四种情况的贡献加在一起就做完了,时空复杂度均为 \(O(n)\)。

代码看着长,有很多都可以复制粘贴,写起来也还好。

#include<bits/stdc++.h>
using namespace std;

using ll=long long;
const int N=2e6+5;

int n;ll d;
ll a[N];

int pre[N],nxt[N],f[N],g[N],buc[N];
ll ans;

void Solve(){
	cin>>n>>d;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	int l=n,r=1;
	for(int i=1;i<=n;i++){
		while(r<n&&a[r+1]-a[i]<=d)r++;
		nxt[i]=r;
	}
	for(int i=n;i>=1;i--){
		while(l>1&&a[i]-a[l-1]<=d)l--;
		pre[i]=l;
	}
	l=1;r=0;
	for(int i=1;i<=n;i++){
		f[i]=1;
		if(pre[i]==i)continue;
		while(r<i-1)buc[nxt[++r]]++;
		while(l<pre[i]+1)buc[nxt[l++]]--;
		f[i]+=buc[i];
		if(nxt[pre[i]]==i)f[i]+=f[pre[i]];
	}
	for(int i=0;i<=n;i++)buc[i]=0;
	l=n+1;r=n;
	for(int i=n;i>=1;i--){
		g[i]=1;
		if(nxt[i]==i)continue;
		while(l>i+1)buc[pre[--l]]++;
		while(r>nxt[i]-1)buc[pre[r--]]--;
		g[i]+=buc[i];
		if(pre[nxt[i]]==i)g[i]+=g[nxt[i]];
	}
	for(int i=0;i<=n;i++)buc[i]=0;
	int u,v;
	ans=0;
	for(u=1;u<=n;u++){
		v=nxt[nxt[u]];
		if(v==nxt[u]||pre[v]!=nxt[u]-1)continue;
		if(pre[pre[v]]==u)ans+=1ll*f[u]*g[v];
		if(pre[pre[v]]<u)ans+=g[v];
	}
	for(v=n;v>=1;v--){
		u=pre[pre[v]];
		if(u==pre[v]||pre[v]!=nxt[u]-1)continue;
		if(nxt[nxt[u]]>v)ans+=f[u];
	}
	l=1;r=0;
	for(int i=1;i<=n;i++){
		if(nxt[i]==i||nxt[i]>=nxt[nxt[i]])continue;
		while(r<nxt[nxt[i]]-1&&pre[pre[r+1]]<i)buc[pre[++r]]++;
		while(l<=nxt[i])buc[pre[l++]]--;
		if(l<=r)ans+=buc[nxt[i]-1];
	}
	for(int i=0;i<=n;i++)buc[i]=0;
	cout<<ans<<'\n';
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int c,t;
	cin>>c>>t;
	while(t--){
		Solve();
	}
	return 0;
}
posted @ 2026-11-05 15:19  z_Sqr  阅读(12)  评论(0)    收藏  举报