P3736 [HAOI2016] 字符合并

题意

有一个长度为 \(n\)\(01\) 串,你可以每次将相邻的 \(k\) 个字符合并,获得一定价值。
给出所有长度为 \(k\) 的串串被合并所获得的价值以及得到的新字符,问把原串缩完能获得的最大价值。
\(n\le300,k\le8\)

思路

\(f_{i,j,s}\) 表示把 \([i,j]\) 的字符缩成 \(s\) 的最大价值(要缩到最短)。转移的时候枚举分界点 \(k\)\(k\) 左边的状态 \(s\),并保证 \(k\) 右边只会缩成一个点,这样是不会漏的,那么 \(f_{l,r,s\times2+t}=max(f_{l,k,s}+f_{k+1,r,t})\) 其中 \(t\in\set{0,1}\)。如果当前区间缩完长度只剩 \(1\),那么需要特殊处理,因为按照上面的转移式处理会把当前区间缩成长度 \(k\)

代码

// Problem: P3736 [HAOI2016] 字符合并
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3736
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
#define LL long long
const int maxn=310,maxk=10,maxzt=300;
int n,k,w[maxzt],c[maxzt],a[maxn];
LL f[maxn][maxn][maxzt];
signed main(){
    read(n,k);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=0;i<(1<<k);i++) read(c[i],w[i]);
    memset(f,-0x3f,sizeof(f));
    for(int i=1;i<=n;i++) f[i][i][a[i]]=0;
    for(int len=1;len<=n;len++) for(int l=1;l<=n;l++){
        int r=l+len;
        if(r>n) break;
        int sy=len%(k-1);
        if(sy==0) sy=k-1;
        for(int w=r-1;w>=l;w-=k-1) for(int s=0;s<(1<<sy);s++){
            f[l][r][s<<1]=max(f[l][r][s<<1],f[l][w][s]+f[w+1][r][0]);
            f[l][r][s<<1|1]=max(f[l][r][s<<1|1],f[l][w][s]+f[w+1][r][1]);
        }
        if(sy+1==k){
            LL g[2]={0,0};
            for(int s=0;s<(1<<sy+1);s++) g[c[s]]=max(g[c[s]],f[l][r][s]+w[s]);
            memset(f[l][r],-0x3f,sizeof(f[l][r]));
            f[l][r][0]=g[0],f[l][r][1]=g[1];
        }
    }
    LL ans=0;
    for(int i=0;i<(1<<k);i++) ans=max(ans,f[1][n][i]);
    write(ans);
    return 0;
}
posted @ 2026-04-29 20:24  Link-Cut_Trees  阅读(21)  评论(0)    收藏  举报