UOJ #757. 【IOI2022】鲶鱼塘
这么简单的题目写了半天,吃枣药丸。
首先我们发现在一个连续上升的长堤我们只需要考虑相邻两个点之间的部分即可,连续下降同理,则可以设\(f_{i,j,0/1}\)表示到了第\(i\)行,这一行覆盖到第\(j\)列,现在在上升/下降的最大值。
然而当你把\(O(n^3)\)的暴力dp写出来以后发现错了/youl。因为如果一个点低于两边任意一个点,则这个点上面的会被算两次。
可以用一个贪心解决这个问题。因为这个点两边都高于它,所以这个点对于两边没有贡献。因此这个点的长堤高度为\(0\)的时候一定不劣。因此对于每个状态可以枚举\(i-2\)处的状态转移。这样就比较正确。
这时我们发现对于每个点有效的值只有相邻三个点鲤鱼的高度,因此总状态数是\(O(m)\)的。而转移可以双指针,因此总复杂度可以做到\(O(m\log m+n)\),其中\(\log\)是排序的复杂度。但似乎vector时间比排序还大的说。
code:
#include "fish.h"
#include<bits/stdc++.h>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=1e5+5,M=(1<<20)+5,K=1e5+5,mod=998244353,Mod=mod-1;const db eps=1e-5;
int n,m,k,x,y,z;ll Ans;
struct Node{int x,w;};bool cmp(Node x,Node y){return x.x<y.x;}vector<Node> Id[N];vector<int> S[N];vector<ll> f[N],g[N];
ll max_weights(int Ns,int Ms,vector<int> X,vector<int> Y,vector<int> W) {
int i,j;n=Ns;m=Ms;for(i=0;i<m;i++) X[i]++,Y[i]++,Id[X[i]].PB((Node){Y[i],W[i]}),S[X[i]].PB(Y[i]-1),S[X[i]+1].PB(Y[i]),S[X[i]-1].PB(Y[i]);
for(i=1;i<=n;i++) S[i].PB(0),S[i].PB(n),sort(S[i].begin(),S[i].end()),S[i].erase(unique(S[i].begin(),S[i].end()),S[i].end()),sort(Id[i].begin(),Id[i].end(),cmp);
for(i=1;i<=S[1].size();i++) f[1].PB(0),g[1].PB(0);for(i=2;i<=n;i++){//cerr<<f[i].size()<<'\n';
int R1=-1,R2=-1;ll Ts=-1e18;for(int j:S[i]){
while(R1+1<S[i-1].size()&&S[i-1][R1+1]<=j){R1++;while(R2+1<Id[i-1].size()&&Id[i-1][R2+1].x<=S[i-1][R1]) Ts+=Id[i-1][++R2].w;Ts=max(Ts,f[i-1][R1]);}
while(R2+1<Id[i-1].size()&&Id[i-1][R2+1].x<=j)Ts+=Id[i-1][++R2].w;/*cout<<j<<' '<<Ts<<' '<<R2<<' '<<Id[i-1][R2+1].w<<'\n';*/f[i].PB(Ts);
}
R1=S[i-1].size();R2=Id[i].size();Ts=-1e18;for(j=S[i].size()-1;~j;j--){
while(R1-1>=0&&S[i-1][R1-1]>=S[i][j]){R1--;while(R2-1>=0&&Id[i][R2-1].x>S[i-1][R1]) Ts+=Id[i][--R2].w;Ts=max(Ts,g[i-1][R1]);}
while(R2-1>=0&&Id[i][R2-1].x>S[i][j]) Ts+=Id[i][--R2].w;g[i].PB(Ts);
}reverse(g[i].begin(),g[i].end());
for(int j=0;j<S[i].size();j++) g[i][j]=max(f[i][j],g[i][j]);if(i==2) continue;
/*for(int j=0;j<S[i].size();j++){
while(R1+1<S[i-2].size()&&S[i-2][R1+1]<=S[i][j]) {R1++;while(R2+1<Id[i-1].size()&&Id[i-1][R2+1].x<=S[i-2][R1])Ps+=Id[i-1][++R2].w;Ts=max(Ts,g[i-2][R1]+Ps);}
f[i][j]=max(f[i][j],Ts);
}R1=S[i-2].size();R2=Id[i-1].size();Ts=-1e18;Ps=0;for(Node p:Id[i-1]) Ps+=p.w;for(j=S[i].size()-1;~j;j--){
while(R1-1>=0&&S[i-2][R1-1]>=S[i][j]) Ts=max(Ts,g[i-2][--R1]);while(R2-1>=0&&Id[i-1][R2-1].x>S[i][j]) Ps-=Id[i-1][--R2].w;f[i][j]=max(f[i][j],Ts+Ps);
}*/
R1=-1;R2=-1;Ts=-1e18;ll Ps=0;for(j=0;j<S[i].size();j++){
while(R1+1<S[i-2].size()&&S[i-2][R1+1]<=S[i][j]) Ts=max(Ts,g[i-2][++R1]);while(R2+1<Id[i-1].size()&&Id[i-1][R2+1].x<=S[i][j]) Ps+=Id[i-1][++R2].w;f[i][j]=max(f[i][j],Ts+Ps);
}R1=S[i-2].size();R2=Id[i-1].size();Ts=-1e18;Ps=0;for(Node p:Id[i-1]) Ps+=p.w;for(j=S[i].size()-1;~j;j--){
while(R1-1>=0&&S[i-2][R1-1]>=S[i][j]) {R1--;while(R2-1>=0&&Id[i-1][R2-1].x>S[i-2][R1]) Ps-=Id[i-1][--R2].w;Ts=max(Ts,Ps+g[i-2][R1]);}f[i][j]=max(f[i][j],Ts);
}for(int j=0;j<S[i].size();j++) g[i][j]=max(f[i][j],g[i][j]);
}
for(ll i:f[n]) Ans=max(Ans,i);for(ll i:g[n]) Ans=max(Ans,i);return Ans;
}