[Ztrening-718][BALKAN OI 2009]Reading 【矩阵加速】

本文介绍了一种基于字符差异度计算单词差异度的方法,并通过构建特殊矩阵实现快速计算,解决了给定最大差异度值时,寻找差异度小于等于该值的所有可能单词数量的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<span style="font-family: 'Microsoft YaHei'; font-size: 18px; background-color: rgb(255, 255, 255);">题目描述:</span>

字符之间形状都有一个相似程度。例如“l”和“i”, “a”和“o”的相似度高,而“x”和“m”的相似度就低。我们定义字符间的形状差异度为1~5等五个等级。相似度高的字符对的差异度低。反之,相似度低的字符对的差异度高。注意,我们规定相同的字符之间的差异度为1!
以此为基础,我们可以定义一个单词的差异度,即:相邻字符之间的差异度之和。
例如,我们规定“e”和“l”的差异度是3,“l”和“y” 的差异度是2,“i”和“l”的差异度是1。这样单词“elly”的差异度为: 3 + 1 + 2 = 6(记住相同字符之间的差异度是1)。单词“lily”的差异度是4,而单字符单词“i”的差异度是1。长的单词的差异度不一定比短的单词的大。例如,“lilii”的差异度仅为4,而“elle”的则有7。但是,一个单词每增加一个字符就至少会增加差异值1。
我们想知道,当给定单词差异度的最大值N时,会有多少个单词的差异度小于等于N呢?

样例输入:

20 10 
e l 3 
e o 1 
o n 2 
o r 4 
r a 4 
i n 5 
e n 2 
n t 3 
t w 3 
w i 5

样例输出:

470059518


思路:

先orz 全能神syk的题解……

这是一道矩阵加速的题目,差异值仅存在于相邻的两个字母之间,于是

转换题目

有26个点,每两个点之间都有一条路径(完全图),给出其中几条路径的长度(length<=5),没有给出的路径默认为1,求该图中长度小于N的路径条数。


基数矩阵(mtx)的构造方法:

设字母i与字母j的差异值为d[i][j],因为矩阵的经典模型之一【限定路径长度求两点之间的路径数】中控制的是路径条数(也可以是路径长度,条件是所有路径长度为1),而这里差异值基本上都>1,不pre()好像没有什么常人的方法控制差异值的大小。于是将每个节点虚拟成一条短链5个节点(见代码),则mtx[i][j+26*(d-1)]=1,而每个mtx[j+26*d][j+26*(d-1)]=1,中间平白无故的多出了d条路径,这样就成功将差异值转化为路径了只有当指数k>=d时i才有可能到达j。


还有一个问题就是,没有严格规定长度必须为N,而是小于N,解决方案:虚拟一个0节点,到每个字母的路径长度为1。但是这样计算的话一旦0节点在路径中的位置不同,计算的时候条数就会++(实际上是不会的,如{_A_B_C}与{_AB_C},因为0节点在最后都不会显示出来,真正起作用的是0节点的个数而非位置),为了避免这种情况,集中将0节点放在开头(0节点可以到0节点和任意节点,任意节点不能到0节点)。


最后枚举起点和终点计算条数。


最后提供一个在syk题解中发现的一个小数据:

1 1 
a b 2 
700

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long int
const int mod=1000000007;
const int maxn=26*5;
const int maxm=26;
using namespace std;
ll n;
int x;
char op[10];
bool vis[maxm+5][maxm+5];
struct Matrix
{
	int e[maxn+5][maxn+5];
}mtx;
void pre()
{
	for(int i=1;i<=26;i++)
		for(int j=1;j<=4;j++)
			mtx.e[i+26*j][i+26*(j-1)]=1;
}
void mul(Matrix a,Matrix b,Matrix &ret)
{
	memset(ret.e,0,sizeof(ret.e));
	for(int i=0;i<=maxn;i++)
		for(int j=0;j<=maxn;j++)
			for(int k=0;k<=maxn;k++)
			{
				ret.e[i][j]=((ll)ret.e[i][j]+(ll)a.e[i][k]*b.e[k][j])%mod;
			}
}
void pow(Matrix a,ll b,Matrix &res)
{
	for(int i=1;i<=maxn;i++)
		res.e[i][i]=1;
	while(b)
	{
		if(b&1)mul(a,res,res);
		mul(a,a,a);
		b>>=1;
	}
}
int main()
{
	scanf("%I64d%d\n",&n,&x);
	pre();
	for(int i=1;i<=x;i++)
	{
		gets(op);
		int t1=op[0]-'a'+1,t2=op[2]-'a'+1,d=op[4]-'0';
		mtx.e[t1][t2+26*(d-1)]=1;
		mtx.e[t2][t1+26*(d-1)]=1;
		vis[t1][t2]=1;
		vis[t2][t1]=1;
	}
	for(int i=1;i<=26;i++)
		for(int j=1;j<=26;j++)
			if(!vis[i][j])mtx.e[i][j]=1;
	for(int i=0;i<=26;i++)
		mtx.e[0][i]=1;
	int sum=0;
	Matrix ans;
	memset(ans.e,0,sizeof(ans.e));
	pow(mtx,n,ans);
	for(int i=0;i<=26;i++)
		for(int j=1;j<=26;j++)
		{
			sum+=ans.e[i][j];
			if(sum>=mod)sum-=mod;
		}
	printf("%d",sum);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值