[AHOI2002]Kitty猫基因突变

· · 题解

以下设 n=2^k

首先分析 T 函数性质,我们可以将 T 函数抽象为在线段树上求解,每次如果整个区间全 0 或全 1cnt++ 并返回(其实就是按照题意模拟)。那么 cnt 的值就是 T 序列内 AB 的个数和。

按照套路,设 $f_{i,j}$ 表示前 $i$ 个基因单元,修改了 $j$ 个 的 $A(s,s')$ 最小值。 记 `sum1[]` 为 前 $i$ 个基因单位全部修改为 $1$ 的代价和, `sum2[]` 为 前 $i$ 个基因单位里 $0$ 的个数。 选择一段区间,全部修改为 $1$ : $$f_{i,j}=\min_{\text{p转移到i合法}}f_{p,j-(sum2[i]-sum2[p])}+(sum1[i]-sum1[p])+2$$ 选择一段区间,全部修改为 $0$ : $$f_{i,j}=\min_{\text{p转移到i合法且区间[p+1,i]内全为0}}f_{p,j}+2$$ 最后的问题是如何求出 $p$ 转移到 $i$ 是否合法。 我们可以发现只有线段树上的区间转移是合法的,我们仿照建树过程,将所有合法转移求出。 ```cpp void build(int l,int r) { t[l-1].push_back(r);//t[x]表示x可以往后转移的点 if(l==r)return; int mid=(l+r)>>1; build(l,mid); build(mid+1,r); } ``` 复杂度 $O(nw)$ ,可以轻松通过。 全部代码(请注意这里的 `sum2[]` 是 `1` 的个数,转移使用的是从前往后推,非上述): ```cpp #include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; int n,w; int c[100001],sum1[100001],sum2[100001]; vector<int>t[100001]; int f[1001][31]; void build(int l,int r) { t[l-1].push_back(r); if(l==r)return; int mid=(l+r)>>1; build(l,mid); build(mid+1,r); } int main() { memset(f,0x3f,sizeof(f)); cin>>n>>w; n=(1<<n); for(int i=1;i<=n;i++) { scanf("%1d",&c[i]); } for(int i=1;i<=n;i++) { int x; cin>>x; sum1[i]=sum1[i-1]+(!c[i])*x; sum2[i]=sum2[i-1]+c[i]; } build(1,n); f[0][0]=0; for(int i=0;i<n;i++) { for(int j=0;j<=w;j++) { for(int k=0;k<t[i].size();k++) { int r=t[i][k]; if(sum2[r]==sum2[i])f[r][j]=min(f[r][j],f[i][j]+2); int s=r-i-(sum2[r]-sum2[i]); if(j+s<=w)f[r][j+s]=min(f[r][j+s],f[i][j]+(sum1[r]-sum1[i])+2); } } } cout<<f[n][w]-1; return 0; } ```