【容斥原理】wikioi 1778 统计公共子序列个数

题目链接:http://wikioi.com/problem/1778/

分析:

我们先思考,当只有一个序列时,如何求出其有多少个不重复的子序列?显然可以定义F[I]表示这个序列的前I位有多少个不同的子序列,转移方程很简单,因为第I个字母可以接在某序列的后面,也可以不接,自然就是F[I]=F[I-1]*2。这样我们会发现答案就是2^长度。但是,这显然是错的。因为我们没有判重!

要知道如何去重,就必须知道出现重复序列的原因!

设当前位S[I]=’a’,并且存在一个最大的J,使得J

现在我们可以将问题扩展到复杂情况了,有类似的思想,可以轻易想到转移方程,当A[I]=B[J]=C[K]时,显然

F[I,J,K]=F[I-1,J-1,K-1]*2-F[II-1,JJ-1,KK-1],其中II、JJ、KK分别为三个序列在I、J、K位前的最靠近该位的位置,并且A[I]=A[II],B[J]=B[JJ],C[K]=C[KK]。貌似到此问题得到解决了,但是写程序的时候会发现,多序列和单序列还是有不同的!

多序列和单序列不同之处在于:多序列需要考虑字符不相等的时候的转移!必须将前面的状态全部转移过来,又不能重复,有什么思想可以解决??

容斥原理!!

所以可以得到状态转移方程:

F[I,J,K]=

F[I-1,J,K]+F[I,J-1,K]+F[I,J,K-1]

-F[I-1,J-1,K]-F[I-1,J,K-1]-F[I,J-1,K-1]

+F[I-1,J-1,K-1]。

特别提示:这样的做法包括空序列,显然由一个字符构成的序列时由空序列转移而来的,所以输出答案时需要减去这个空序列。另外,此题要用高精,压位。

代码:

#include
#include
#include
#include
#define ff 1000000000
using namespace std;
int n;
struct node
{
	int s[10];
}  f[151][151][151];
inline void jian( int  i, int  j, int k, int i1,int j1,int k1 )
{
	for (int ji=1;ji<=9;ji++)
	 {
	 	f[i][j][k].s[ji]-=f[i1][j1][k1].s[ji];
	 	if (f[i][j][k].s[ji]<0)
	 	 {
	 	 	f[i][j][k].s[ji+1]-=1;
	 	 	f[i][j][k].s[ji]+=ff;
	 	 }
	 }
} 
inline void jia( int  i, int  j, int k, int i1,int j1,int k1,int kk)
{
    for (int ji=1;ji<=kk;ji++)
	for (int ki=1;ki<=9;ki++)
	 {
	 	f[i][j][k].s[ki]+=f[i1][j1][k1].s[ki];
	 	if (f[i][j][k].s[ki]>=ff)
	 	 {
	 	 	f[i][j][k].s[ki+1]+=(f[i][j][k].s[ki]/ff);
	 	 	f[i][j][k].s[ki]%=ff;
	 	 }
	 }
}
int main()
{   
    string a=" ",b=" ",c=" ",aa,bb,cc;
   int i,j,k;
	cin>>n;
	cin>>aa>>bb>>cc;
	a+=aa;b+=bb;c+=cc;
	for(i=0;i<=n;++i)
	for(j=0;j<=n;++j)
	{
		f[0][i][j].s[1]=1;
		f[i][j][0].s[1]=f[i][0][j].s[1]=f[0][i][j].s[1];
	}
	for (i=1;i<=n;i++)
	 for (j=1;j<=n;j++)
	  for (k=1;k<=n;k++)
	   {
	   	if (a[i]==b[j]&&b[j]==c[k])
	   	 {
	   	 	jia(i,j,k,i-1,j-1,k-1,2);
	   	    int ji=i-1; while (a[ji]!=a[i]&&ji>0) ji--;
	   	    int jj=j-1; while (b[jj]!=b[j]&&jj>0) jj--;
	   	    int jk=k-1; while (c[jk]!=c[k]&&jk>0) jk--;
		    if(ji>0&&jj>0&&jk>0) jian(i,j,k,ji-1,jj-1,jk-1);  
	     }
	    else
	     {
	     	jia(i,j,k,i-1,j,k,1); 
	     	jia(i,j,k,i,j-1,k,1); 
	     	jia(i,j,k,i,j,k-1,1);
	     	jia(i,j,k,i-1,j-1,k-1,1);
	     	jian(i,j,k,i-1,j-1,k);
	     	jian(i,j,k,i,j-1,k-1);
	     	jian(i,j,k,i-1,j,k-1);
		 }
	   }
	jian(n,n,n,0,0,0);
	int jiji=9; while (f[n][n][n].s[jiji]==0) jiji--;
	cout<=1;i--) printf("%.9d",f[n][n][n].s[i]);
	return 0;
}




你可能感兴趣的:(递推,数论,高精)