BZOJ2216: [Poi2011]Lightning Conductor
Dp·决策单调性
题解:
pi=max{aj−|i−j|−−−−−√}−ai
只想出一个
O(nn√logn)
的暴力QwQ
我们发现对于一个i,
|i−j|−−−−−√
的上取整是分段的,这样的段有
O(n√)
个,因此枚举一下,线段树查询这个区间最值,更新答案即可。
然后hzw大爷用一个 O(nn√) 的做法狂虐本蒟蒻QwQ而且很好写QwQ
就是把线段树查询换成滑动窗口维护。因为段的长度是固定的,就相当于滑动窗口维护一个定长区间的最值,然后用它直接更新相应的 pi .
注意两处//take care!。滑动窗口要完全滑出去,才能更新所有左边要更新的f[].
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
const int N = 200005;
int f[N],n,a[N];
struct Queue{
int d[N]; int head,tail;
void clear(){ head=tail=0; }
void autopop(int lim){ while(head<tail && d[head]<=lim)head++; }
void push(int i){ while(head<tail && a[d[tail-1]]<=a[i])tail--; d[tail++]=i; }
int front(){ return d[head]; } bool empty(){ return head==tail; }
} q;
void work(int len,int lp,int rp,int d){
// D(len); D(lp); D(rp); D(d); E;
q.clear();
for(int i=1;i<=n+len;i++){ //take care!
// D(i); E;
q.push(i); q.autopop(i-len);
int mx=a[q.front()]+d;
// D(q.front()); D(mx); E;
// D(i-lp); D(i+rp); E;
if(i-lp>0) f[i-lp]=max(f[i-lp],mx);
if(i+rp<=n)f[i+rp]=max(f[i+rp],mx);
}
}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",a+i);
int lp,rp;
for(int i=1;i*i<=(n<<1);i++){ //take care!
lp=i*i; rp=(i-1)*(i-1)+1; work(i*i-(i-1)*(i-1),lp,rp,i);
}
for(int i=1;i<=n;i++)printf("%d\n",max(0,f[i]-a[i]));
}
正解:决策单调性!
假如k
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
const int N = 1000005;
int n;
int a[N], ans[N];
double f1[N], f2[N];
struct Data{
int id,l,r;
Data(){}
Data(int _id,int _l,int _r){ id=_id; l=_l; r=_r; }
} q[N];
double cal(int j,int i){
return a[j]-a[i]+sqrt(i-j);
}
void dp(double f[]){
Data *head=q, *tail=q;
*tail=Data(1,2,n); tail++;
for(int i=2;i<=n;i++){
while(head<tail && head->r < i) head++;
f[i]=cal(head->id, i);
head->l = i;
while(head<tail && head->l > head->r) head++;
while(head<tail && cal((tail-1)->id,(tail-1)->l) < cal(i,(tail-1)->l)) tail--;
if(head<tail){
int l=(tail-1)->l, r=(tail-1)->r, mid;
while(l<=r){
mid=(l+r)>>1;
if(cal((tail-1)->id,mid) < cal(i,mid)) r=mid-1;
else l=mid+1;
}
++r;
if(r<=n){
(tail-1)->r = r-1;
*tail=Data(i,r,n); tail++;
}
}
else{
*tail=Data(i,i+1,n); tail++;
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
dp(f1);
reverse(a+1,a+1+n);
dp(f2);
// for(int i=1;i<=n;i++) D(f1[i]); E;
// for(int i=n;i>=1;i--) D(f2[i]); E;
for(int i=1;i<=n;i++) ans[i]=ceil(max(f1[i],f2[n-i+1]));
// for(int i=1;i<=n;i++) D(ans[i]), E;
for(int i=1;i<=n;i++) printf("%d\n",max(ans[i],0));
}