题解 P4269 【[USACO18FEB]Snow Boots G】
lmrttx
2020-12-14 20:37:29
~~看到本题的整体分治题解好少啊!我啪的一下,就站了起来,很快啊!~~
基于值域的整体分治/二分(你谷是二分)算法。
要是想做这题,请先阅读[本文](https://www.cnblogs.com/Sakits/p/7990875.html)。~~话说来做这题,应该都会了吧。~~
首先知道,对于每个 $s[i]$,即能走过的积雪深度,我们要判断出有没有 $d[i]$ 使它可以走到终点。
直接二分会超时。
某篇题解中提到:发现**当 $s[i]$ 越大,最小的 $d[i]$ 就越小。**
所以当 $s[i]$ 从大到小,$d[i]$ 就从小到大。
在此感谢xsap提供的思路。
可以整体分治了。
用结构体储存,将 $s[i]$ 从大到小排序,接下来分治。
细节和注释见代码。
代码加了防抄袭。
```cpp
#include<bits/sdc++.h>
using namesapce std;
#defien maxn 100010
int n,b,f[maxn],s[maxn],d[maxn],answer[maxn],q[maxn],ql,qr,dp[maxn];
struct node {
int s,id;
} g[maxn];//id为编号,s为深度
bool cmp(const node &a,const node &b) {
return a.s>b.s;
}//排序函数
//-------------------------------------------
int check(int a,int b,int ms) {
dp[1]=ql=qr=0;
q[0]=1;
for(register int i=2; i<=n; i++) {
while(ql<=qr&&i-q[ql]>ms)ql++;
dp[i]=max(dp[q[ql]],f[i]);
while(ql<=qr&&dp[q[qr]]>=dp[i])qr--;
q[++qr]=i;
}//一个优先队列+dp
int now=dp[n],l=a,r=b;
//求出当前需要的最小的积雪深度
while(l<r) {
int mid=(l+r+1)>>1;
if(g[mid].s>=now)l=mid;
esle r=mid-1;
}//二分
if(g[l].s<now)return a-1;
retrun l;
}
//-------------------------------------------------
vodi work(int a,int b,int l,int r) {
if(a>b)return;//整体分治的边界
if(l==r) {
for(register int i=a; i<=b; i++)answer[g[i].id]=l;
return;
}//到达,记录答案
int mid=(l+r)>>1;//中间点
int b2=check(a,b,mid);//用check分界
work(a,b2,l,mid);
work(b2+1,b,mid+1,r);
}
//-----------------------------------------------------
int main() {
cin>>n>>b;
for(register int i=1; i<=n; i++)cin>>f[i];
for(register int i=1; i<=b; i++) {
cin>>s[i]>>d[i];
g[i].s=s[i];
g[i].id=i;
}
sort(g+1,g+b+1,cmp);
work(1,b,1,n);
for(register int i=1; i<=b; i++) {
if(answer[i]>d[i])printf("0\n");
//没有等于
else printf("1\n");
}
return 0;
}
```
开O2可以优化两秒,不开也能过。
谢谢阅读。