P7152 [USACO20DEC] Bovine Genetics G

首先有一个 \(O(n^2)\) 的 dp。

设计状态 \(f_{i,0/1/2/3}\) 表示前 \(i\) 个字符以 \(A,C,G,T\) 中哪一个结尾的方案数。

\(i\) 要从 \(j\) 转移需要满足 \((i,j)\) 内没有相邻相同的字符。记 \(lst_i\) 表示前 \(i\) 位最后一个满足 \(s_i=s_{i-1}\) 的位置。

\(f_{i,x}=\sum_{j\ge lst-1} \sum_y f_{j,y}w(i,j,x,y)\)

这个转移系数就是 \((j,i)\) 中所有问号的可能方案。

考虑问号串,其可取的方案需要分情况讨论,分别是两端字符相等或不等,这可以简单 dp 得到。

则长度为 \(i\) 的问号串的方案数形如。

状态 \(w_{i,0/1/2/3}\) 表示前 \(i\) 个问号,结尾问号为 \(A,C,G,T\) 中一者的方案数。

则初始化第一位不能等于该问号串前端的字符,方案数即为 \(w_{n,0}+w_{n,1}+w_{n,2}+w_{n,3}-w_{n,B}\),其中 \(B\) 为问号串右端的字符。

转移 \(i\) 时,\(j\) 从大往小遍历每次新加入一个字符维护转移系数即可做到 \(O(n^2)\)。具体细节比较多,实现比较繁琐。

假设问号串前字符为 \(A\),将 \(w_{i,A}\) 列出观察:

\(0,3,6,21,60,\cdots\)

容易发现 \(g_1=0\),递推式 \(g_i=3g_{i-1}-3(-1)^i\)

\(g_i-3g_{i-1}=-3(-1)^i=3(-1)^{i+1}\)

\(g_i=g_i-3g_{i-1}+3g_{i-1}-9g_{i-2}+\cdots \\=3(-1)^{i+1}+9(-1)^i+27(-1)^{i-1}+\cdots\)

实际上可以看做公比为 -3 的等比数列求和。

\(g_n=\frac{(-3)^{n-1}3(-1)^{n+1}-3(-1)^{n+1}}{-3-1}=\frac{3^n+3(-1)^{n}}{4}\)

\(w_{i,j},j\ne A\) 列出观察:

\(1,2,7,20,61,\cdots\)

容易发现 \(h_1=1\),递推式 \(h_i=3h_{i-1}-(-1)^i\)

同理能推出:

\(h_n=\frac{3^n+(-1)^{i+1}}{4}\)

每个问号串对不在该串内的位置的贡献是相同的,对于在串内的貌似是可以根据奇偶性分别维护的,所以感觉是可以前缀和优化的,不过转移细节很多。

只有 \(O(n^2)\) 代码了,后者爆炸难调,就先咕着吧。

#include<bits/stdc++.h>
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define fr(x) fin(x),fout(x);
#define Fr(x,y) fin(x),fout(y)
#define INPUT(_1,_2,FILE,...) FILE
#define IO(...) INPUT(__VA_ARGS__,Fr,fr)(__VA_ARGS__)
using namespace std;
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cfast ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define ll long long
#define ull unsigned long long
#define intz(x,y) memset((x),(y),sizeof((x)))
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
#define tup(x) array<int,(x)>
inline ll read(){
    ll x=0,f=1;char ch=nc();
    while(ch<48||ch>57){if(ch=='-')f=-1;ch=nc();}
    while(ch>=48&&ch<=57)x=x*10+ch-48,ch=nc();
   	return x*f;
}
//void write(int x){cout<<x<<' ';}
//void write(pii x){cout<<"P("<<x.fi<<','<<x.se<<")\n";}
//void write(vector<auto>x){for(auto i:x)write(i);cout<<'\n';}
//void write(auto *a,int l,int r){for(int i=l;i<=r;i++)write(a[i]);cout<<'\n';}
inline ll lowbit(ll x){return x&-x;}
inline int pcount(ll x){
	for(int i=0,res=0;;res+=(x>>i)&1,i++)
		if(i>60)return res;
}
const int mod=1e9+7;
struct mt{
	ll v;
	mt(){v=0;}
	mt(int x){this->v=x;}
	inline mt operator+(mt x){return {(v+x.v)%mod};}
	inline mt operator-(mt x){return {(v+mod-x.v)%mod};}
	inline mt operator*(mt x){return {1ll*v*x.v%mod};}
};
mt qp(mt x,int y){mt res(1);for(;y;x=x*x,y>>=1)if(y&1)res=res*x;return res;}
const int N=1e5+5;
mt f[N][4],g[N][5][5],w[N][4],iw[N][4];
int lst[N],cnt[N];char s[N];
int c(char x){
	if(x=='A')return 0;
	else if(x=='G')return 1;
	else if(x=='C')return 2;
	else if(x=='T')return 3; 
	else return 4;
}
inline void UesugiErii(){
	cin>>(s+1);int n=strlen(s+1);lst[0]=1;
	for(int i=1;i<=n;i++)
		lst[i]=(s[i]==s[i-1]&&s[i]!='?'?i:lst[i-1]),
		cnt[i]=cnt[i-1]+(s[i]=='?');
	for(int i=0;i<4;i++)
		for(int j=0;j<4;j++)if(i^j)
			g[1][i][j]=1;
	for(int k=2;k<=n;k++)
		for(int i=0;i<4;i++){
			for(int j=0;j<4;j++)
				w[k-1][i]=w[k-1][i]+g[k-1][i][j];
			iw[k-1][i]=qp(w[k-1][i],mod-2);
			for(int j=0;j<4;j++)
				g[k][i][j]=w[k-1][i]-g[k-1][i][j];
		}
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++)
			w[n][i]=w[n][i]+g[n][i][j];
		iw[n][i]=qp(w[n][i],mod-2),iw[0][i]=1;
	}
	for(int i=0;i<4;i++)f[0][i]=1;
	for(int i=1;i<=n;i++){
		int p=c(s[i]),S=(s[i]=='?'?0:p),T=(s[i]=='?'?3:p);
		for(int y=S;y<=T;y++){
			mt sum=1;int ed=0,lid=y;
			for(int j=i-1;j>=lst[i]-1;j--){
				if(j==i-2&&y==c(s[i-1]))break;
				if(s[j+1]=='?')
					for(int x=0;x<4;x++){
						if((j+2==i&&x==y)||(j+1<i&&x==c(s[j+2]))||(i==j+1&&(x^y)))continue;
						mt tmp=sum;
						if(ed)tmp=tmp*iw[ed][lid]*(w[ed][lid]-g[ed][lid][x]);
						f[i][x]=f[i][x]+f[j][y]*tmp;
					}
				else{
					mt tmp=sum;
					if(ed)tmp=tmp*iw[ed][lid]*(w[ed][lid]-g[ed][lid][c(s[j+1])]);
					f[i][c(s[j+1])]=f[i][c(s[j+1])]+f[j][y]*tmp;
				}
				if(j+1<i&&s[j+1]=='?')
					sum=sum*w[ed+1][lid]*iw[ed][lid],++ed;
				else if(s[j+1]!='?'){
					if(ed)sum=sum*iw[ed][lid]*(w[ed][lid]-g[ed][lid][c(s[j+1])]);
					lid=c(s[j+1]),ed=0;
				}
			}
		}
	}
	mt ans=0;
	for(int i=0;i<4;i++)
		ans=ans+f[n][i];
	cout<<ans.v;
}
signed main(){
	//IO();
	cfast;
	int _=1;//cin>>_;
	for(;_;_--)UesugiErii();
	return 0;
}
posted @ 2025-11-18 21:08  Uesugi1  阅读(8)  评论(0)    收藏  举报