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

来源:互联网 发布:家庭千兆网络布线 编辑:程序博客网 时间:2024/06/05 21:12

题目传送门: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;}