队爷的Au Plan CH Round #59

题目: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
",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 }
View Code

       赛后发现别人的代码都很短,瞬间惊呆

       出题人说: 

       对于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
",f[n]);
46     return 0;
47 }
View Code

出题人没卡线段树真是太良心了。好评!

原文地址:https://www.cnblogs.com/zyfzyf/p/4069108.html