短路
发现路线只会向右或下走,且一定是走到了某一个层环后沿环绕到右下然后用来时相同的花费走回去
枚举走到哪一个环,线性处理出走到这个环的最小花费
dp出左上走到第i层环左上角的最小花费,算贡献时贪心找1~i-1中最小的环
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;
const int maxn = 110000;
int n;
int a[maxn]; ll sum[maxn];
ll f[maxn];
ll ans;
int main()
{
ans=LLONG_MAX;
scanf("%d",&n); ++n;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int mn;
for(int i=n;i>=1;i--)
{
sum[i]=sum[i+1]+a[i];
if(i==n) f[i]=a[i],mn=i;
else
{
f[i]=f[mn]+(ll)(mn-i)*a[mn]+sum[i]-sum[mn];
if(a[i]<a[mn]) mn=i;
}
if(mn==i) ans=min(ans,f[i]*2ll+(ll)(4*i-5)*a[i]);
}
printf("%lld\n",ans);
return 0;
}
天路
对于每个区间长度k,求所有长度为k的区间中最大值减最小值的最大值
因为只要求与标准答案的花费不超过5%,不妨取
c=1.050,1.051,1.052,1.05...
因为随着k递增,对应的答案一定递增,对每个ci,找最小的x满足k>=x的k的答案都>=ci,那么夹在ci和ci+1之间的k的答案都可以用ci代替
ci的值的个数是logV的,枚举每个ci,用n的时间维护两个队列或预处理之后rmq,O(n)找到x
O(nlogV)
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e6
using namespace std;
const int maxn = 210000;
const double eps = 1e-7;
int n,u;
int na,ma,a[maxn];
int r[maxn];
int q1[maxn],h1,t1;
int q2[maxn],h2,t2;
double f[maxn];
int main()
{
scanf("%d",&n); na=inf;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
na=min(na,a[i]); ma=max(ma,a[i]);
}u=ma-na;
double c=1.0;
while(c<=u+eps)
{
h1=1,t1=0;
h2=1,t2=0;
int kk=0;
for(int i=1;i<=n;i++)
{
r[i]=r[i-1];
int cc=a[i];
while(h1<=t1&&a[q1[t1]]>=cc-eps) t1--;
while(h1+1<=t1&&cc-a[q1[h1+1]]>=c-eps) h1++;
if(h1<=t1&&cc-a[q1[h1]]>=c-eps) r[i]=max(r[i],q1[h1]);
q1[++t1]=i;
while(h2<=t2&&a[q2[t2]]<=cc+eps) t2--;
while(h2+1<=t2&&a[q2[h2+1]]-cc>=c-eps) h2++;
if(h2<=t2&&a[q2[h2]]-cc>=c-eps) r[i]=max(r[i],q2[h2]);
q2[++t2]=i;
kk=max(kk,i-r[i]);
}
f[kk+1]=c;
if(c<=100) c+=1.0;
else c*=1.05;
}
for(int i=2;i<=n;i++)
{
if(fabs(f[i])<eps) f[i]=f[i-1];
printf("%d\n",(int)(f[i]+0.5));
}
return 0;
}
套路
对于所有区间长度k>=K,求(区间[l,r]内值两两差的绝对值最小值s(l,r)*(r-l))的最大值
当区间长度k较小时,枚举k,用f[k][i]代表s(i,i+k-1),那么有
f[k][i]=min(f[k−1][i],f[k−1][i+1],abs(a[i]−a[i+k−1]))
当区间长度k<=S时,处理出这些区间的答案复杂度是
O(nS)
的
当k>S时,由鸽巢原理可得
s(l,r)<=m/(S−1)
,可以只考虑差值<=m/(S-1)的值两两之间的贡献
用r[c]表示c最后出现的位置,f[i][k]表示以i为右端点,s(l,r)=k的左端点最左位置,有
f[i][k]=max(f[i−1][k],f[i][k−1],r[c+−k])
枚举区间右端点扫过去,对于当前的ai,枚举与其差值<=m/(S-1)的数统计这些数对答案的贡献,复杂度O(nm/S)
令
S=m−−√
平衡复杂度,总复杂度
O(nm−−√)
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
inline int _min(const int a,const int b){return a<b?a:b;}
inline void up(int &a,const int &b){if(a<b)a=b;}
inline void down(int &a,const int &b){if(a>b)a=b;}
const int maxn = 210000;
int n,m,K,S,u;
int a[maxn];
int f[maxn];
int las[maxn],r[maxn];
int ans[maxn];
int main()
{
scanf("%d%d%d",&n,&m,&K); S=sqrt(m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int j=1;j<=n;j++) f[j]=m;
for(int i=2;i<=S;i++)
{
for(int j=1;j+i-1<=n;j++)
{
down(f[j],_min(f[j+1],abs(a[j]-a[j+i-1])));
up(ans[i],f[j]);
}
}
u=m/max(1,S-1)+1;
for(int i=1;i<=n;i++)
{
int cc=a[i];
for(int j=0;j<=u;j++)
{
int &temp=r[j];
if(cc-j>=1) up(temp,las[cc-j]);
if(cc+j<=m) up(temp,las[cc+j]);
up(r[j+1],temp);
up(ans[i-temp],j+1);
}
las[cc]=i;
}
ll re=0;
for(int i=K;i<=n;i++) re=max(re,(ll)(i-1)*ans[i]);
printf("%lld\n",re);
return 0;
}