zoukankan      html  css  js  c++  java
  • CF506E Mr. Kitayuta's Gift

    一、题目

    点此看题

    二、解法

    使我深受洗礼的一道题,是既有思维难度又有代码难度不可多得的好题

    先考虑偶回文串吧!首先考虑如何计数,题目都告诉你只关心最终状态,我们直接对最终状态计数。考虑枚举法确定原来字符在最终序列的位置,那么可以简单计数来确定方案。

    (dp) 优化这个过程,设 (f_{i,l,r}) 为考虑最终序列的前 (i) 个和后 (i) 个字符,原串还剩下区间 ([l,r]) 没有确定位置的方案数,设 (g_i) 表示考虑最终序列的前 (i) 个和后 (i) 个字符,原串已经用完的方案数。

    为了不算重我们需要把某种方案只使用某种方式表示,就算它可以通过多种"方式"表示出来,我们也规定一种方式来储存它:

    (s_l=s_r,r-lleq 1),则可以直接到终状态,(f)(g) 转移,还可以不同地填字符对((25) 种):

    [g_{i+1}leftarrow f_{i,l,r} ]

    [f_{i+1,l,r}leftarrow 25cdot f_{i,l,r} ]

    (s_l=s_r,r-lgeq 2),则可以把两边取出来配对,也可以不同地填字符对((25) 种):

    [f_{i+1,l+1,r-1}leftarrow f_{i,l,r} ]

    [f_{i+1,l,r}leftarrow 25cdot f_{i,l,r} ]

    (s_l ot=s_r),则可以取出一边再拿个任意字符来配对,也可以不同地填字符对((24) 种):

    [f_{i+1,l+1,r}leftarrow f_{i,l,r} ]

    [f_{i+1,l,r-1}leftarrow f_{i,l,r} ]

    [f_{i+1,l,r}leftarrow 24cdot f_{i,l,r} ]

    最后是 (g) 的转移,可以有 (26) 种任意的填法:(g_{i+1}leftarrow 26cdot g_i)


    上面的转移可以直接套矩阵乘法,时间复杂度 (O(m^6log n))

    请容许我盗波图,继续优化我们可以画出转移的图像,下图的红点表示左右端点消减 (1) 的转移点,绿点表示左右端点消减 (2) 的转移点,答案就是这个图上起点到终点的路径方案数:

    img

    因为原串大小固定,如果一条路径上有 (k) 个红点,那么就必须有 (lfloorfrac{m-k}{2} floor) 个绿点。而路径的方案数是和自环紧密相关的,也就是和经过的红点和绿点个数相关的,而和权值只有 (1) 的边没有太大关系,所以说本质不同的路径条数只有 (O(m)) 条。

    所以可以统计每种路径的出现次数,对它单独矩乘,简单乘法原理就可以算出答案,时间复杂度 (O(m^4log m)),统计路径的那一步可以用记忆化搜索,设 (h_{i,l,r})(i) 个绿点,考虑区间 ([l,r]) 的方案数。

    进一步优化可以考虑整体 (dp),因为它们的转移规则是相同的,所以我们建在一张图上:

    img

    其中从后往前第 (i) 个红点(/)绿点就表示路径上还需要走 (i) 个红点(/)绿点,连接红点和绿点之间的边就设置成这种路径的条数即可,注意没有红点需要初始化在绿点上,用矩阵加速跑这张图时间复杂度 (O(m^3log n))

    如果最后是奇回文串,那么可以把不合法的情况减去,也就是最后一步通过长度为 (2) 的绿点转移到终点的情况,那么我们去掉终点的自环,再只保留到这些绿点的路径,同样跑一遍矩乘就可以出答案。

    实现小细节:由于我们建出的图是上三角(只会由编号小的转移到编号大的),所以做乘法的时候也可以只乘上三角,据说常数直接小到 (frac{1}{6})

    后记:好像可以猜本题有线性递推,先用 ( t BM) 解出来,验证之后直接上常系数齐次线性递推:传送门

    三、总结

    本题我所缺失的就是那步关键的去重处理,方案如何表示很重要,如果只关心最终方案,那么过程中有多种方法可以强制通过某一种方法达到最终方案。

    其他设计到的技巧:设计图论模型直观表示问题;等价类的划分;转移方式相同使用整体 (dp)

    本题的模型其实也可以称为有限状态自动机,字符串问题可以朝这个方向思考一下。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int M = 505;
    const int MOD = 1e4+7;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k,g[M][M],f[M],h[M][M][M];char s[M];
    int ceil(int x)
    {
    	return (x>>1)+(x&1);
    }
    int dfs(int i,int l,int r)
    {
    	if(i<0 || l>r) return 0;
    	if(h[i][l][r]!=-1) return h[i][l][r];
    	int &o=h[i][l][r];o=0;
    	if(l==1 && r==m) return o=!i;
    	if(l>1 && r<m && s[l-1]==s[r+1])
    		o=(o+dfs(i,l-1,r+1))%MOD;
    	if(l>1 && s[l-1]!=s[r])
    		o=(o+dfs(i-1,l-1,r))%MOD;
    	if(r<m && s[l]!=s[r+1])
    		o=(o+dfs(i-1,l,r+1))%MOD;
    	return o;
    }
    void qkpow(int b)
    {
    	while(b>0)
    	{
    		if(b&1)
    		{
    			int a[M]={};
    			for(int i=1;i<=k;i++)
    			for(int j=1;j<=k;j++)
    				a[i]=(a[i]+f[j]*g[j][i])%MOD;
    			swap(a,f);
    		}
    		int a[M][M]={};
    		for(int i=1;i<=k;i++)
    		for(int j=i;j<=k;j++)
    		for(int l=j;l<=k;l++)
    			a[i][l]=(a[i][l]+g[i][j]*g[j][l])%MOD;
    		swap(a,g);
    		b>>=1;
    	}
    }
    signed main()
    {
    	scanf("%s",s+1),m=strlen(s+1);n=read();
    	memset(h,-1,sizeof h);k=m+ceil(m);
    	//[1,m) red ; [m,k) green ; k end
    	for(int i=0;i<m;i++)
    	{
    		int c=0;
    		for(int j=1;j<=m;j++)
    		{
    			c=(c+dfs(i,j,j))%MOD;
    			if(j<m && s[j]==s[j+1])
    				c=(c+dfs(i,j,j+1))%MOD;
    		}
    		if(!i)
    		{
    			f[m]=c;g[k][k]=26;
    			for(int j=m;j<k;j++)
    				g[j][j+1]=1,g[j][j]=25;
    		}
    		else
    		{
    			g[i][k-ceil(m-i)]=c;g[i][i]=24;
    			if(i>1) g[i-1][i]=1;
    			else f[i]=1;
    		}
    	}
    	int F[M]={},G[M][M]={};
    	memcpy(F,f,sizeof F);
    	memcpy(G,g,sizeof G);
    	qkpow(ceil(n+m));
    	if((n+m)%2==0)//even length
    	{
    		printf("%d
    ",f[k]);
    		return 0;
    	}
    	int ans=f[k];
    	memcpy(f,F,sizeof f);
    	memcpy(g,G,sizeof g);
    	//build the new graph,containing illegal roads
    	for(int i=0;i<m;i++)
    	{
    		int c=0;
    		for(int j=1;j<m;j++) if(s[j]==s[j+1])
    			c=(c+dfs(i,j,j+1))%MOD;
    		if(i) g[i][k-ceil(m-i)]=c;
    		else f[m]=c,g[k][k]=0;
    	}
    	qkpow(ceil(n+m));
    	printf("%d
    ",(ans-f[k]+MOD)%MOD);
    }
    
  • 相关阅读:
    upc组队赛3 T-net【贪心】
    upc组队赛5 Assembly Required【思维】
    upc组队赛5 Bulbs
    upc组队赛5 Ingenious Lottery Tickets【排序】
    upc组队赛5 Hunter’s Apprentice 【判断多边形边界曲线顺逆时针】
    upc组队赛5 Ground Defense【枚举】
    upc组队赛4 Go Latin
    upc组队赛4 TV Show Game 【2-SAT】
    POJ 3250 Bad Hair Day【单调栈入门】
    016.NET5_MVC_视图组件扩展定制
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15505807.html
Copyright © 2011-2022 走看看