给你一个字符串,每次从串首或串尾拿出一个到一个新字符串的队尾,求字典序最小的串。
若队首队尾不一样直接贪心,否则你枚举队首还是队尾,看看谁更优即可。
约翰的2N(3<=N<=1000)只奶牛正打算举办一场音乐会来吸收资金。其中,N只奶牛是手风琴手,而另N只奶牛是班卓琴手。每个手风琴手有一个天才指数Ai(0<=Ai<=1000),同样一个班卓琴手有一个天才指数Bi(0<=Bi<=1000)。
约翰打算给手风琴手和班卓琴手配对。如果让手风琴手i与班卓琴手j配对,她们吸收的资金将是Ai * Bj。所以约翰可以用他的聪明才智选择一种最好的搭配方式,让奶牛们吸收的总资金最大化。
不幸的是,约翰的手风琴手有一个怪癖:如果手风琴手i已经和班卓琴手j配对,那手风琴手i+1到N,绝不肯与班卓琴手1到j-1间的某一个配对。这让约翰的搭配计划造成了很多不便。而且,约翰也意识到,适当地让一些奶牛放弃配对,不要参加音乐会,会让吸收的总资金更大。
然而,那些失去机会的失落奶牛,为了消解她们的孤独与痛苦,正成群结队地到就把去喝冰镇橙味酒。如果从x号到y号手风琴手均是失落的奶牛(x-1号和y+1号奶牛不存在或参加了演出),这y-x+1只奶牛会结成一伙去喝酒,而且她们的消费量是
同样,对于班卓琴手也有这样的规律。
面对未来可能产生的巨额酒费支出,约翰不得不再认真考虑他的配对计划了。请你帮忙计算最大的收益是多少?
先有一个四方的dp,然后发现dp[i][j]只能从dp[i-1][j1]或dp[i1][j-1]转移,因为如果有空的话可以再差一个。然后就是玩具装箱般的斜率优化。
太工业了,必须贴代码。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define X first
#define Y second
#define MP make_pair
#define pii pair
#define LL long long
#define DB double
#define INF LONG_LONG_MAX/1000
#define pb push_back
#define sqr(_) ((_)*(_))
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
template void Read(T& x)
{
x=0;int flag=0,sgn=1;char c;
while(c=getchar())
{
if(c=='-')sgn=-1;
else if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
else if(flag)break;
}
x*=sgn;
}
#define int LL
const int MAXN=2001;
int A[MAXN],B[MAXN],dp[MAXN][MAXN],n,s[2][MAXN];
int q[MAXN],first,last;
int ans=-INF;
struct Queue1{
int q[MAXN],first,last;
Queue1(){
first=last=0;
q[last++]=1;
}
DB slope(int id,int x,int y)
{
if(s[1][y]==s[1][x])
{
DB k=dp[id][x]-dp[id][y]-sqr(s[1][x])+sqr(s[1][y]);
if(k==0)return 0;
return k>0?INF:-INF;
}
return (DB)(dp[id][x]-dp[id][y]-sqr(s[1][x])+sqr(s[1][y]))/(DB)(s[1][y]-s[1][x]);
}
void insert(int id,int i)
{
while(first<last-1&&(DB)slope(id,q[last-2],q[last-1])>=(DB)slope(id,q[last-1],i))
last--;
q[last++]=i;
}
void work(int id,int i,int j)
{
while(first<last-1&&(DB)slope(id,q[first],q[first+1])<=(DB)2*s[1][j-1])
first++;
dp[i][j]=max(dp[i][j],dp[id][q[first]]-sqr(s[1][j-1]-s[1][q[first]])+A[i]*B[j]);
ans=max(ans,dp[i][j]-sqr(s[0][n]-s[0][i])-sqr(s[1][n]-s[1][j]));
}
}q1[MAXN];
struct Queue2{
int q[MAXN],first,last;
Queue2(){
first=last=0;
q[last++]=1;
}
DB slope(int id,int x,int y)
{
if(s[0][x]==s[0][y])
{
DB k=dp[x][id]-dp[y][id]-sqr(s[0][x])+sqr(s[0][y])>=0;
if(k==0)return 0;
return k>0?INF:-INF;
}
return (DB)(dp[x][id]-dp[y][id]-sqr(s[0][x])+sqr(s[0][y]))/(DB)(s[0][y]-s[0][x]);
}
void insert(int id,int i)
{
while(first<last-1&&(DB)slope(id,q[last-2],q[last-1])>=(DB)slope(id,q[last-1],i))
last--;
q[last++]=i;
}
void work(int id,int i,int j)
{
while(first<last-1&&(DB)slope(id,q[first],q[first+1])<=(DB)2*s[0][i-1])
first++;
dp[i][j]=max(dp[i][j],dp[q[first]][id]-sqr(s[0][i-1]-s[0][q[first]])+A[i]*B[j]);
ans=max(ans,dp[i][j]-sqr(s[0][n]-s[0][i])-sqr(s[1][n]-s[1][j]));
}
}q2[MAXN];
main()
{
Read(n);
for(int i=1;i<=n;i++)
Read(A[i]);
for(int i=1;i<=n;i++)
Read(B[i]);
for(int i=1;i<=n;i++)s[0][i]=s[0][i-1]+A[i],s[1][i]=s[1][i-1]+B[i];
for(int i=0;ifor(int j=0;j0][0]=0;
s[1][n+1]=s[1][n];
s[0][n+1]=s[0][n];
for(int i=1;i<=n;i++)
{
dp[1][i]=A[1]*B[i]-sqr(s[1][i-1]);
ans=max(ans,dp[1][i]-sqr(s[0][n]-s[0][1])-sqr(s[1][n]-s[1][i]));
dp[i][1]=A[i]*B[1]-sqr(s[0][i-1]);
ans=max(ans,dp[i][1]-sqr(s[0][n]-s[0][i])-sqr(s[1][n]-s[1][1]));
for(int i=2;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(j!=1)q1[i-1].work(i-1,i,j);
if(j!=1)q2[j-1].work(j-1,i,j);
q1[i-1].insert(i-1,j);
q2[j].insert(j,i-1);
}
cout<
给定一个长度为N的数列{an},你需要按照将这个数列分割成若干个子段,要求每一段的累加和不能超过给定的M,并且你需要求出每一段最大值的累加和,显然你必须保证累加和越小越好。
设dp[i]为答案,dp[i]=min(dp[j]+max(j+1,i)。
我们用线段树转移,每个节点维护区间内最小的dp值和最小的dp[j]+max(j+1,i)值。
预处理出每个数往左以它为最大值最多到哪里l[i]。
每扫到一个数,在线段树里面区间改即可。
以为还剩30min的时候以为第三题不太好做,其实好傻逼啊,当时颓废的心已经起来了,不想想题了。。。还有就是第二题肛太久,没有发现三方的dp,何谈斜率优化。第一题如果想了超过70min赶紧弃疗,第二三题谁简单就一直肛谁。