2016 多校3 1009 Palindrome Bo 回文串dp+优化

题意 给一个串,要求找出有多少个从里到外递增(不严格)的回文串序列(即可以分开的),最大长度是多少


做法:很容易想到维护一个n3的区间dp
dp[l][r]  更新是枚举c(c<=a[l])  dp[ nxt[l][c] ][ pre[r][c] ]
nxt[pos][c]代表pos位置向右第一个值为c的位置
pre[pos][c]代表pos位置向左第一个值为c的位置
但是会t


考虑n2优化
我们发现,如果固定l左端点,移动右端点,每次移动一次,nxt[l]这个数组在移动过程中是不会变的
pre[r]数组随着r->r+1,每次也只会变一个值,就是新加入的r+1
那么我们考虑先反着扫l,在正着搞r,这样扫过的所有子区间都被我提前处理过
从前到后维护一个ans最优解

每次到达r时,枚举的那个c应该是越来越优的,所以我只需要把原来的pre[r][a[r]]的影响(如果有)减掉。再加上r的影响即可

#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <cstdio>
#include <cstring>
#include <vector>
#include <cstdlib>
#include <stack>
#include <algorithm>
#include <queue>
#include <map>
#define xx first
#define yy second
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;

ll dp[5005][5005], f[5005][5005];
int nxt[5005][5005], pre[5005][5005];
ll a[5005];
pair<ll,int>san[5005];

int main()
{
    int n, i, j, m, l, r;
    freopen("in.in", "r", stdin );
    while(~scanf("%d", &n)){
        for( i = 1; i <= n; i ++ ){
            scanf("%I64d", &a[i]);
            san[i].xx = a[i];
            san[i].yy = i;
        }
        sort( san+1, san+1+n );//离散化
        int cnt = 0;
        san[0].xx = -1;
        for( i = 1; i <= n; i ++ ){
            if( san[i].xx != san[i-1].xx ) cnt ++;
            a[ san[i].yy ] = cnt;
        }
        //for( i = 1; i <= n; i ++ ) printf("%I64d  ", a[i]);
        a[0] = a[n+1] = cnt+1;
        memset( dp, -1, sizeof(dp) );
        memset( nxt, 0, sizeof(nxt) );
        memset( pre, 0, sizeof(pre) );
        for( i = 0; i <= n+1; i ++ ){
            for( j = i+1; j <= n+1; j ++ ){
                if( !nxt[i][a[j]] ) nxt[i][a[j]] = j;
            }
            for( j = i-1; j >= 0; j -- ){
                if( !pre[i][a[j]] ) pre[i][a[j]] = j;
            }
        }
        for( l = n+1; l >= 0; l -- ){//  倒过来搞
            ll ans1 = 0, ans2 = 1;
            for( r = l; r <= n+1; r ++ ){
                ll tmp1, tmp2;
                if( l == r ){
                    dp[l][r] = 1;
                    f[l][r] = 1;
                    continue;
                }
                if( a[r-1] <= a[l] ){
                    tmp1 = dp[ nxt[l][a[r-1]] ][ pre[r-1][a[r-1]] ];
                    tmp2 = f[ nxt[l][a[r-1]] ][ pre[r-1][a[r-1]] ];
                    if( ans1 == tmp1 ){
                        ans2 = ans2-tmp2+mod;
                        ans2 %= mod;
                    }
                    if( ans1 < tmp1 ){
                        ans1 = tmp1;
                        ans2 = tmp2;
                    }//在更新之前,我必须先去除前一个右边为a[r]的留下的影响
                    tmp1 = dp[ nxt[l][a[r-1]] ][ r-1 ];
                    tmp2 = f[ nxt[l][a[r-1]] ][ r-1 ];
                    if( ans1 == tmp1 ){
                        ans2 += tmp2;
                        ans2 %= mod;
                    }
                    if( ans1 < tmp1 ){
                        ans1 = tmp1;
                        ans2 = tmp2;
                    }
                }
                if( a[l] == a[r] ){
                    dp[l][r] = ans1 + 2;
                    f[l][r] = ans2;
                }
                else dp[l][r] = 0, f[l][r] = 0;
            }
        }
        ll ans1 = 1, ans2 = 1;
//        for( i = 1; i <= n; i ++ ){
//            for( j = i; j <= n; j ++ ){
//                printf("dp %d %d  %I64d   %I64d\n", i, j, dp[i][j], f[i][j]);
//            }
//        }
        for( i = 1; i <= cnt; i ++ ){
            if( dp[ nxt[0][i] ][ pre[n+1][i] ] > ans1 ){
                ans1 = dp[ nxt[0][i] ][ pre[n+1][i] ];
                ans2 = f[ nxt[0][i] ][ pre[n+1][i] ];
            }
            else if( ans1 == dp[ nxt[0][i] ][ pre[n+1][i] ] ){
                ans2 += f[ nxt[0][i] ][ pre[n+1][i] ];
                ans2 %= mod;
            }
        }
        printf("%I64d %I64d\n", ans1, ans2);
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值