队爷的Au Plan CH Round #59 - OrzCC杯NOIP模拟赛day1
题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的Au%20Plan
题解:看了题之后觉得肯定是DP+优化,因为昨天刚水了一道线段树优化DP的题,所以又想到线段树上去了。。。
具体做法:
我维护了一个单调递增的f,显然若i<j并且f[i]>f[j],那么f[j]就可以不用
然后我们要找寻>=a[i]的就是连续的一段了,就可以用线段树来查询f[j]-s[j]的最大值了
然后 n*logn 水过,居然拿下了first blood
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #include<string> 12 #define inf 1000000000 13 #define maxn 250000 14 #define maxm 500+100 15 #define eps 1e-10 16 #define ll long long 17 #define pa pair<int,int> 18 #define for0(i,n) for(int i=0;i<=(n);i++) 19 #define for1(i,n) for(int i=1;i<=(n);i++) 20 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 21 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 22 #define mod 1000000007 23 using namespace std; 24 inline int read() 25 { 26 int x=0,f=1;char ch=getchar(); 27 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 28 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 29 return x*f; 30 } 31 int n,m,tot,a[maxn],b[maxn],f[maxn]; 32 struct seg{int l,r,mx;}t[4*maxn]; 33 void build(int k,int l,int r) 34 { 35 t[k].l=l;t[k].r=r;int mid=(l+r)>>1;t[k].mx=-inf; 36 if(l==r)return; 37 build(k<<1,l,mid);build(k<<1|1,mid+1,r); 38 } 39 void change(int k,int x,int y) 40 { 41 int l=t[k].l,r=t[k].r,mid=(l+r)>>1; 42 if(l==r){t[k].mx=max(t[k].mx,y);return;} 43 if(x<=mid)change(k<<1,x,y);else change(k<<1|1,x,y); 44 t[k].mx=max(t[k<<1].mx,t[k<<1|1].mx); 45 } 46 int query(int k,int x,int y) 47 { 48 int l=t[k].l,r=t[k].r,mid=(l+r)>>1; 49 if(l==x&&r==y)return t[k].mx; 50 if(y<=mid)return query(k<<1,x,y); 51 else if(x>mid)return query(k<<1|1,x,y); 52 else return max(query(k<<1,x,mid),query(k<<1|1,mid+1,y)); 53 } 54 int main() 55 { 56 freopen("input.txt","r",stdin); 57 freopen("output.txt","w",stdout); 58 n=read();m=read(); 59 for1(i,n)a[i]=read(); 60 for1(i,n)b[i]=b[i-1]+read(); 61 build(1,0,n); 62 f[tot=0]=m;change(1,0,m); 63 //for1(i,4*n)cout<<i<<' '<<t[i].l<<' '<<t[i].r<<' '<<t[i].mx<<endl; 64 for1(i,n) 65 { 66 int x=lower_bound(f,f+tot+1,a[i])-f,y; 67 if(f[x]<a[i])continue;else y=query(1,x,tot); 68 //for0(j,tot)cout<<j<<' '<<f[j]<<endl; 69 //cout<<i<<' '<<x<<' '<<y<<endl; 70 if(i==n){printf("%d\n",y+b[i]-a[i]);return 0;} 71 if(y+b[i]-a[i]>f[tot]) 72 { 73 //cout<<tot<<' '<<f[tot]<<endl; 74 f[++tot]=y+b[i]-a[i]; 75 change(1,tot,f[tot]-b[i]); 76 } 77 } 78 return 0; 79 }
赛后发现别人的代码都很短,瞬间惊呆
出题人说:
对于i状态,考虑j,k两个决策,j<k<i且f[j]>hard[i]&&f[k]>hard[i]。
若j,k不为前面同一个决策转移得到,则k的决策层数显然会更大,即额外消耗更多,所以j优于k;
若j,k为前面同一个决策l转移得到:
f[j]=f[l]-sum[l]+sum[j]-hard[j]
f[k]=f[l]-sum[l]+sum[k]-hard[k]
f[j]-f[k]=sum[j]-sum[k]+hard[k]-hard[j]
f[j]-sum[j]-(f[k]-sum[k])=hard[k]-hard[j]
即f[j]-sum[j]> f[k]-sum[k],
所以j决策优于k决策, 所以对于每一个状态i,最小的能够转移到i的决策总是最优的,这样只需要用单调队列维护决策即可(其实一个指针就OK了),时间复杂度为O(n)。
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #include<string> 12 #define inf 1000000000 13 #define maxn 250000 14 #define maxm 500+100 15 #define eps 1e-10 16 #define ll long long 17 #define pa pair<int,int> 18 #define for0(i,n) for(int i=0;i<=(n);i++) 19 #define for1(i,n) for(int i=1;i<=(n);i++) 20 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 21 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 22 #define mod 1000000007 23 using namespace std; 24 inline int read() 25 { 26 int x=0,f=1;char ch=getchar(); 27 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 28 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 29 return x*f; 30 } 31 int n,m,tot,a[maxn],b[maxn],f[maxn]; 32 int main() 33 { 34 freopen("input.txt","r",stdin); 35 freopen("output.txt","w",stdout); 36 n=read();f[0]=read(); 37 for1(i,n)a[i]=read(); 38 for1(i,n)b[i]=b[i-1]+read(); 39 int j=0; 40 for1(i,n) 41 { 42 while(f[j]<a[i])j++; 43 f[i]=f[j]+b[i]-b[j]-a[i]; 44 } 45 printf("%d\n",f[n]); 46 return 0; 47 }
出题人没卡线段树真是太良心了。好评!