1. 程式人生 > >BZOJ 4367 [IOI2014]holiday (決策單調DP+主席樹+分治) [WF2017]Money for Nothing

BZOJ 4367 [IOI2014]holiday (決策單調DP+主席樹+分治) [WF2017]Money for Nothing

題目大意:略 題目傳送門

神題,不寫長題解簡直是浪費了這道題

 

貪心

考慮從0節點出發的情況,顯然一直往前走不回頭才是最優策略

如果起點是在中間某個節點$s$,容易想到,如果既要遊覽$s$左邊的某些景點,又要遊覽$s$右邊的某些景點,最優策略一定是先遊覽完一邊,然後再穿過$s$節點去遊覽另一邊

也就是說$s$節點一定只被穿過一次,且先被遊覽完的一邊還要額外消耗$x$點代價,$x$是距離$s$最遠的被遊覽的節點到s的距離

 

設計DP狀態

現在只考慮從S出發往右走的情況

定義狀態$dp[i][j]$表示第i天,現在在j點時最多遊覽的景點數

容易得到轉移方程$dp[i][j]=max(dp[i-|j-k|][k]+|j-k|+a_{j})$

暴力列舉 $O(n^{3})$,線段樹優化$DP\;\;O(n^{2}logn)$

兩種狀態的空間是$O(n^{2})$的,不論是空間還是時間都不可行

利用上述貪心結論,我們一定一直往前走不回頭,除非我們回頭到$s$然後走另一邊

所以j這一維不用記錄了,因為在路上走的距離總和是已知的

除了向右走的情況,還有向左走,向右走回到景點$s$,向左走回到景點$s$另外三種情況,設它們為$f[i][0],g[i][0],f[i][1],g[i][1]$,轉移都很類似不過多贅述

縮減狀態,$dp[i]$表示走了$i$天最多遊覽的景點數

列舉遊覽的最右側端點$j$,$dp[i]$就是在$S$到$j$之間選擇最大的$i-(j-S)$個景點

可以用可持久化權值線段樹實現,每次查詢在主席樹上二分,時間$O(logn)$

 

決策單調性

上述過程依然沒有解決時間$O(n^{2})$這一難題

假設我們現在要求解$f[i]$,即走了i天最多遊覽的景點數

暴力列舉所有位置,找到了決策位置$p$,

那麼走$j \leq i-1$天時,$f[j]\leq f[i]$,且$f[j]$選擇的點的集合一定是$S$的子集,$f[j]$的決策位置一定$\leq p$

走$j \geq i+1$天時,$f[j]\geq f[i]$,且$f[j]$選擇的點的集合一定包含$S$,$f[j]$的決策位置一定$\geq p$

發現決策竟然是單調的

 

分治

用類似於[WF2017]Money for Nothing的方法分治

但這道題對有多個決策位置的處理方式略有不同

在分治過程中,設現在天數的分治區間是$[l1,r1]$,選擇要求解$f[mid]$,可能的決策區間是$[l2,r2]$

暴力列舉$[l2,r2]$,找到了$f[mid]$的決策集合$T$,$T$中的每個元素都能作為$f[mid]$的決策,設其中最靠右的元素是b

$[l1,mid-1]$可能的決策區間是$[l2,b]$,因為並不知道哪個決策位置對應的點集合$S$的子集最優,所以$f[mid]$整個決策區間都可能作為$[l1,mid-1]$的決策

$[mid+1,r1]$可能的決策區間是$[b,r2]$,而不是集合裡最靠左的元素到$r2$,下面給出簡單證明

現在點權序列是2 8 9 1 3 11,走7步,最優方案有兩種,決策位置是5,對應的點集$S$是8 9 3,決策位置是6,對應的點集$S$是9 11

容易發現,更靠右的決策位置,除了對應點集S裡的點,剩餘的權值較大的節點更多

$f[mid]$決策位置是5時,對應的點集S是8 9 3,還能被取的點是2 1

$f[mid]$決策位置是6時,對應的點集S是9 11,還能被取的點是8 2 1 3

因為大家都是貪心取,決策位置左邊的最大的都取走了

 

最後把四種情況合併即可

另外這道題卡空間,需要對權值離散,不離散的話主席樹樹高會非常高,空間上容易被卡

別忘了離散只是為了優化線段樹的結構,統計的時候把實際值還原回來

 

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #define N1 100010
  5 #define M1 250010
  6 #define ll long long
  7 #define dd double
  8 #define inf 23333333333333333ll
  9 using namespace std;
 10  
 11 int gint()
 12 {
 13     int ret=0,fh=1;char c=getchar();
 14     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
 15     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
 16     return ret*fh;
 17 }
 18 int b[N1];
 19 struct Persist_SEG{
 20 int sz[N1*22],ls[N1*22],rs[N1*22],root[N1],tot; ll sum[N1*22];
 21 inline void pushup(int rt)
 22 {
 23     sz[rt]=sz[ls[rt]]+sz[rs[rt]];
 24     sum[rt]=sum[ls[rt]]+sum[rs[rt]];
 25 }
 26 void update(int x,int l,int r,int rt1,int &rt2,int w)
 27 {
 28     if(!rt2||rt2==rt1){ rt2=++tot; sz[rt2]=sz[rt1]; ls[rt2]=ls[rt1]; rs[rt2]=rs[rt1]; sum[rt2]=sum[rt1]; }
 29     if(l==r){ sum[rt2]+=w; sz[rt2]++; return; }
 30     int mid=(l+r)>>1; ll ans=0;
 31     if(x<=mid) update(x,l,mid,ls[rt1],ls[rt2],w);
 32     else update(x,mid+1,r,rs[rt1],rs[rt2],w);
 33     pushup(rt2);
 34 }
 35 ll query(int K,int l,int r,int rt1,int rt2)
 36 {
 37     if(!rt2||!K) return 0;
 38     if(l==r) return min(sum[rt2]-sum[rt1],1ll*K*b[l]);
 39     int mid=(l+r)>>1; ll ans=0;
 40     if(sz[rs[rt2]]-sz[rs[rt1]]<K) ans=sum[rs[rt2]]-sum[rs[rt1]]+query(K-(sz[rs[rt2]]-sz[rs[rt1]]),l,mid,ls[rt1],ls[rt2]); 
 41     else ans=query(K,mid+1,r,rs[rt1],rs[rt2]); 
 42     return ans;
 43 }
 44 }s;
 45  
 46 int n,S,D,ma,de;
 47 int a[N1];
 48 ll f[M1][2],g[M1][2];
 49 void solve_f0(int l1,int r1,int l2,int r2)
 50 {
 51     if(l1>r1||l2>r2) return;
 52     int mid=(l1+r1)>>1,i,p=-1; ll tmp,ans=-inf;
 53     for(i=l2;i<=r2&&i-S<=mid;i++)
 54     {
 55         tmp=s.query(mid-(i-S),1,ma,s.root[S-1],s.root[i]);
 56         if(tmp>ans) ans=tmp,p=i;
 57         else if(tmp==ans) p=i;
 58     }
 59     f[mid][0]=ans;
 60     if(ans==-inf)
 61     {
 62         for(i=mid+1;i<=r1;i++) if(i>=(l2-S)){ solve_f0(i,r1,l2,r2); break; }
 63         return;
 64     }
 65     solve_f0(l1,mid-1,l2,p); solve_f0(mid+1,r1,p,r2);
 66 }
 67 void solve_f1(int l1,int r1,int l2,int r2)
 68 {
 69     if(l1>r1||l2>r2) return;
 70     int mid=(l1+r1)>>1,i,p=-1; ll tmp,ans=-inf;
 71     for(i=l2;i<=r2&&2*(i-S)<=mid;i++)
 72     {
 73         tmp=s.query(mid-2*(i-S),1,ma,s.root[S-1],s.root[i]);
 74         if(tmp>ans) ans=tmp,p=i;
 75         else if(tmp==ans) p=i;
 76     }
 77     f[mid][1]=ans;
 78     if(ans==-inf)
 79     {
 80         for(i=mid+1;i<=r1;i++) if(i>=2*(l2-S)){ solve_f1(i,r1,l2,r2); break; }
 81         return;
 82     }
 83     solve_f1(l1,mid-1,l2,p); solve_f1(mid+1,r1,p,r2);
 84 }
 85 void solve_g0(int l1,int r1,int l2,int r2)
 86 {
 87     if(l1>r1||l2>r2) return;
 88     int mid=(l1+r1)>>1,i,p=-1; ll tmp,ans=-inf;
 89     for(i=r2;i>=l2&&S-i<=mid;i--)
 90     {
 91         tmp=s.query(mid-(S-i),1,ma,s.root[i-1],s.root[S-1]);
 92         if(tmp>ans) ans=tmp,p=i;
 93         else if(tmp==ans) p=i;
 94     }
 95     g[mid][0]=ans;
 96     if(ans==-inf)
 97     {
 98         for(i=mid+1;i<=r1;i++) if(i>=(S-r2)){ solve_g0(i,r1,l2,r2); break; }
 99         return;
100     }
101     solve_g0(l1,mid-1,p,r2); solve_g0(mid+1,r1,l2,p);
102 }
103 void solve_g1(int l1,int r1,int l2,int r2)
104 {
105     if(l1>r1||l2>r2) return;
106     int mid=(l1+r1)>>1,i,p=-1; ll tmp,ans=-inf;
107     for(i=r2;i>=l2&&2*(S-i)<=mid;i--)
108     {
109         tmp=s.query(mid-2*(S-i),1,ma,s.root[i-1],s.root[S-1]);
110         if(tmp>ans) ans=tmp,p=i;
111         else if(tmp==ans) p=i;
112     }
113     g[mid][1]=ans;
114     if(ans==-inf)
115     {
116         for(i=mid+1;i<=r1;i++) if(i>=2*(S-r2)){ solve_g1(i,r1,l2,r2); break; }
117         return;
118     }
119     solve_g1(l1,mid-1,p,r2); solve_g1(mid+1,r1,l2,p);
120 }
121  
122 int main()
123 {
124     scanf("%d%d%d",&n,&S,&D); S++;
125     int i; ll ans=0;
126     for(i=1;i<=n;i++) a[i]=gint(),b[i]=a[i];
127     sort(b+1,b+n+1); ma=unique(b+1,b+n+1)-(b+1);
128     for(i=1;i<=n;i++)
129     {
130         a[i]=lower_bound(b+1,b+ma+1,a[i])-b;
131         s.update(a[i],1,ma,s.root[i-1],s.root[i],b[a[i]]);
132     }
133     solve_f0(1,D,S,n); solve_f1(1,D,S,n); 
134     solve_g0(1,D,1,S-1); solve_g1(1,D,1,S-1);
135     for(i=0;i<=D;i++){ans=max(ans,max( max(f[i][0],g[i][0]) , max(f[i][1]+g[D-i][0],f[i][0]+g[D-i][1]) )); }
136     printf("%lld\n",ans);
137     return 0;
138 }