Codeforces Round #445 Div1 E:Mod Mod Mod (平衡树优化DP)

27 篇文章 0 订阅
12 篇文章 0 订阅

题目传送门:http://codeforces.com/contest/889/problem/E


题目大意:有一个长度为n的序列a。定义函数 f(x,n) 值为 xmodan 。并定义函数 f(x,i)(1<=i<n) 值为 xmodai+f(xmodai,i+1) 。求最大的 f(x,1) n<=2105,1<=ai<=1013


题目分析:一开始想错了,以为是道水题。容易发现最优解必定存在一个i,使得最初的x不断取模下去,到第i位恰好为 ai1 。于是维护一个单调栈,枚举这个 ai1 。往右走的时候二分到第一个小于当前值的地方;往左走的时候贪心地变大,二分第一个大于当前值两倍的地方。由于往左走至少乘以2,往右走至少除以2,时间复杂度为 O(nlog(n)log(1013))

然而往左走贪心地变大是错的,举个反例:
a={13,19,8,2,3,8,10,7,12,6}
当i=4的时候,贪心地往左走,前四个值为{7,7,7,1},然而最优解应该是{11,11,3,1}。

正解的做法是一个很神奇的DP(以下为题解翻译)。设 x=moda1moda2modai ,则f[i][j]=k表示做到第i位,当x取0~j时1~i的总贡献为xi+k,最大化k。设M=a[i+1],则若 j<M ,f[i][j]=k变为f[i+1][j]=k;若j>=M,则f[i][j]=k分为最优的两部分: f[i+1][jmodM]=k+i(jjmodM) f[i+1][M1]=k+i((j+1)/MMM) 。由于M-1是定值,所以保留f[i+1][M-1]的最大值即可,这样就保证做到第i位时,最多有i个状态。又因为j模一个比它小的数,值至多变为原先的一半,所以用map存储这些状态,每一次将大于等于M的j拉出来更新,时间复杂度就是 O(nlog(n)log(1013))

不会用STL的map?还不快像我一样手写Treap!


CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=200100;
const int Lg=40;
const long long M1=998244353;
const long long M2=1000000007;
const long long M3=1333333331;
typedef long long LL;
LL seed;

struct Tnode
{
    LL val,cnt;
    int fix;
    Tnode *lson,*rson;
} tree[maxn*Lg];
Tnode *Root=NULL;
int cur=-1;

LL a[maxn];
int n;
LL ans=0;

int Rand()
{
    seed=(seed*M1+M2)%M3;
    return (int)seed;
}

Tnode *New_node(LL Val,LL Cnt)
{
    cur++;
    tree[cur].val=Val;
    tree[cur].cnt=Cnt;
    tree[cur].fix=Rand();
    tree[cur].lson=tree[cur].rson=NULL;
    return tree+cur;
}

void Right_turn(Tnode *&P)
{
    Tnode *W=P->lson;
    P->lson=W->rson;
    W->rson=P;
    P=W;
}

void Left_turn(Tnode *&P)
{
    Tnode *W=P->rson;
    P->rson=W->lson;
    W->lson=P;
    P=W;
}

void Insert(Tnode *&P,LL Val,LL Cnt)
{
    if (!P) P=New_node(Val,Cnt);
    else
        if ( Val<P->val || ( Val==P->val && Cnt<P->cnt ) )
        {
            Insert(P->lson,Val,Cnt);
            if ( P->lson->fix < P->fix ) Right_turn(P);
        }
        else
        {
            Insert(P->rson,Val,Cnt);
            if ( P->rson->fix < P->fix ) Left_turn(P);
        }
}

Tnode *Last(Tnode *P)
{
    if (!P->rson) return P;
    return Last(P->rson);
}

void Delete(Tnode *&P,LL Val,LL Cnt)
{
    if ( Val==P->val && Cnt==P->cnt )
        if (P->lson)
            if (P->rson)
                if ( P->lson->fix < P->rson->fix )
                {
                    Right_turn(P);
                    Delete(P->rson,Val,Cnt);
                }
                else
                {
                    Left_turn(P);
                    Delete(P->lson,Val,Cnt);
                }
            else P=P->lson;
        else P=P->rson;
    else
        if ( Val<P->val || ( Val==P->val && Cnt<P->cnt ) ) Delete(P->lson,Val,Cnt);
        else Delete(P->rson,Val,Cnt);
}

void Dfs(Tnode *P)
{
    if (!P) return;
    Dfs(P->lson);
    Dfs(P->rson);
    ans=max(ans,(long long)n*P->val+P->cnt);
}

int main()
{
    freopen("E.in","r",stdin);
    freopen("E.out","w",stdout);

    scanf("%d",&n);
    seed=n;
    for (int i=1; i<=n; i++) scanf("%I64d",&a[i]);
    Insert(Root,a[1]-1LL,0LL);
    for (int i=2; i<=n; i++)
    {
        LL Max=-1;
        Tnode *P=Last(Root);
        while ( P->val>=a[i] )
        {
            LL Val=P->val,Cnt=P->cnt;
            Delete(Root,Val,Cnt);
            LL temp=(Val+1LL)/a[i]-1LL;
            Max=max(Max,Cnt+temp*(long long)(i-1)*a[i]);
            if (Val%a[i]!=a[i]-1LL) temp++;
            Insert(Root,Val%a[i],Cnt+temp*(long long)(i-1)*a[i]);
            P=Last(Root);
        }
        if (Max>=0) Insert(Root,a[i]-1LL,Max);
    }
    Dfs(Root);
    printf("%I64d\n",ans);
    //printf("%d\n",sizeof(tree)/1024/1024);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值