4565: [Haoi2016]字符合并 区间DP

fi,j,k 表示区间 [i,j] 合并成 k 的最大收益,其中K=0/1保证 [i,j] 可以合并成一个数。
转移的时候用 gj,k 表示对于当前的 i ,[i,j]合并成了 k ,其中k是一个二进制数。
然后转移一下就行了

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int N=305;
int n,m; 
int a[N],c[N],w[N];
long long f[N][N][2],g[N][N],tmp[2],ans[N];
char s[N];

inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}

int main()
{   
    n=read(); m=read(); 
    memset(f,-1,sizeof(f));
    scanf("%s",s+1);
    for (int i=1;i<=n;i++) f[i][i][s[i]-'0']=0;
    for (int i=0;i<1<<m;i++)
        c[i]=read(),w[i]=read();
    for (int i=n-m+1;i;i--)
    {
        memset(g,-1,sizeof(g));
        int now=1;
        g[i][0]=f[i][i][0];
        g[i][1]=f[i][i][1];
        for (int j=i+1;j<=n;j++)
        {
            for (int s=0;s<1<<now;s++)
                if (g[j-1][s]>=0)
                    for (int k=j;k<=n;k+=m-1)
                    {
                        if (f[j][k][0]>=0)
                            g[k][s<<1]=max(g[k][s<<1],g[j-1][s]+f[j][k][0]);
                        if (f[j][k][1]>=0)
                            g[k][s<<1|1]=max(g[k][s<<1|1],g[j-1][s]+f[j][k][1]);
                    }
            if (++now==m)
            {
                memset(tmp,-1,sizeof(tmp));
                for (int s=0;s<1<<m;s++)
                    if (g[j][s]>=0) 
                        tmp[c[s]]=max(tmp[c[s]],w[s]+g[j][s]);
                f[i][j][0]=g[j][0]=tmp[0];
                f[i][j][1]=g[j][1]=tmp[1];
                now=1;
            }
        }
    }
    for (int i=1;i<=n;i++)
        for (int j=i;j<=n;j+=m-1)
            ans[j]=max(ans[j],ans[i-1]+max(f[i][j][0],f[i][j][1]));
    cout << ans[n] << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这道题目还可以使用树状数组或线段树来实现,时间复杂度也为 $\mathcal{O}(n\log n)$。这里给出使用树状数组的实现代码。 解题思路: 1. 读入数据; 2. 将原数列离散化,得到一个新的数列 b; 3. 从右往左依次将 b 数列中的元素插入到树状数组中,并计算逆序对数; 4. 输出逆序对数。 代码实现: ```c++ #include <cstdio> #include <cstdlib> #include <algorithm> const int MAXN = 500005; struct Node { int val, id; bool operator<(const Node& other) const { return val < other.val; } } nodes[MAXN]; int n, a[MAXN], b[MAXN], c[MAXN]; long long ans; inline int lowbit(int x) { return x & (-x); } void update(int x, int val) { for (int i = x; i <= n; i += lowbit(i)) { c[i] += val; } } int query(int x) { int res = 0; for (int i = x; i > 0; i -= lowbit(i)) { res += c[i]; } return res; } int main() { scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); nodes[i] = {a[i], i}; } std::sort(nodes + 1, nodes + n + 1); int cnt = 0; for (int i = 1; i <= n; ++i) { if (i == 1 || nodes[i].val != nodes[i - 1].val) { ++cnt; } b[nodes[i].id] = cnt; } for (int i = n; i >= 1; --i) { ans += query(b[i] - 1); update(b[i], 1); } printf("%lld\n", ans); return 0; } ``` 注意事项: - 在对原数列进行离散化时,需要记录每个元素在原数列中的位置,便于后面计算逆序对数; - 设树状数组的大小为 $n$,则树状数组中的下标从 $1$ 到 $n$,而不是从 $0$ 到 $n-1$; - 在计算逆序对数时,需要查询离散化后的数列中比当前元素小的元素个数,即查询 $b_i-1$ 位置上的值; - 在插入元素时,需要将离散化后的数列的元素从右往左依次插入树状数组中,而不是从左往右。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值