“May you, the beauty of this world, always shine.”
前言
笑点解析:
if(Le[i+1] == Le[i]) vL += valL[Le[i+1]];
if(Ri[i+1] == Ri[i]) vR += valR[Ri[i]];
赛时代码对于 valL 和 valR 的处理加上这个即可过。痛失 WC。
思路
其实是比较简单的题,所以为什么出题人要在大样例里塞 Corner Case 哦哦哦。
首先注意到方案数为 2 的充要条件当且仅当对于一个点对 \((u,v)\),\(u \to v\) 和 \(v \to u\) 贪心的跳只差一个点。我们容易处理出向左跳和向右跳的极限点,分别记为 \(l,r\),则我们容易对于每一个前缀和后缀,得到前后极限跳方案唯一的路径数量。
然后我们同时不难发现这个不同点是相邻的,否则会有 >2 条路径,显然不合法。然后我们可以枚举这对不同点。但是这里还有一个我赛时没有想到的要求,就是说只有这对相邻点的的向左的极限跳相等,才能继承 \(l\) 的贡献,对于向右的也是同理,否则你无法通过大样例4。考虑如果极限跳不相等,而两点又在路径上,则显然可以更换跳点,形成 >2 条路径。注意到我的做法的证明依赖于这个,但是犯糖导致以为这个会自动合法。
Code
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 2e6 + 10;
int n; LL d;
LL x[N];
int Le[N],Ri[N];
LL valL[N],valR[N];
void solve(){
cin >> n >> d;
for(int i=1;i<=n;i++)
cin >> x[i];
int L=1;
for(int i=1;i<=n;i++){
while(L < i && x[i] - x[L] > d) L ++ ;
Le[i] = L;
}
int R = n;
for(int i=n;i>=1;i--){
while(R > i && x[R] - x[i] > d) R -- ;
Ri[i] = R;
}
for(int i=1;i<=n;i++){
if(Le[i] == Le[i+1]) valL[i] = 0;
else valL[i] = valL[Le[i]] + (Le[i+1] - Le[i]);
}
for(int i=n;i>=1;i--){
if(Ri[i] == Ri[i-1]) valR[i] = 0;
else valR[i] = valR[Ri[i]] + (Ri[i] - Ri[i-1]);
}
LL ans = 0;
for(int i=2;i<n-1;i++){
if(Le[i+1] >= i || Ri[i] <= i + 1) continue;
LL vL,vR;
vL = min(Le[i+2],i) - Le[i+1];
vR = Ri[i] - max(i+1,Ri[i-1]);
if(vL == 0 || vR == 0) continue;
if(Le[i+1] == Le[i]) vL += valL[Le[i+1]];
if(Ri[i+1] == Ri[i]) vR += valR[Ri[i]];
ans += vL * vR;
}
cout << ans << "\n";
}
int main(){
IOS;
int c,T;
cin >> c >> T;
while(T -- )
solve();
return 0;
}
Fun Fact
什么叫我的左边是 jiazhichen844?

浙公网安备 33010602011771号