[6-7]考试总结

年度最佳牛农

给你一个字符串,每次从串首或串尾拿出一个到一个新字符串的队尾,求字典序最小的串。
若队首队尾不一样直接贪心,否则你枚举队首还是队尾,看看谁更优即可。

手风琴与班卓琴

约翰的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<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<map>
#include<queue>
#include<vector>
#include<cassert>
#include<ctime>
#include<climits>
#define X first
#define Y second
#define MP make_pair
#define pii pair<int,int>
#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<class T> 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;i<MAXN;i++)
        for(int j=0;j<MAXN;j++)
            dp[i][j]=-INF;
    dp[0][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<<ans<<endl;
}

数列

给定一个长度为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赶紧弃疗,第二三题谁简单就一直肛谁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值