CF1970E3 Trails (Hard)

Codeforces

对于 Easy 部分的做法:

很容易想到统计下来每个点的位置,枚举到达的点,然后进行转移统计即可。

时间复杂度 \(O(m^2n)\)

由于个人习惯,代码中的 \(n\)\(m\) 与原题目不同。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int n,m,s[100005],l[100005],t[2][100005];
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>s[i];
	for(int i=1;i<=n;i++)cin>>l[i];
	t[0][1]=1;
	int cnt=0;
	for(int i=1;i<=m;i++){
		cnt^=1;
		for(int j=1;j<=n;j++)t[cnt][j]=0;
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				t[cnt][k]+=((t[cnt^1][j]*s[j])%mod*s[k])%mod;
				t[cnt][k]%=mod;
				t[cnt][k]+=((t[cnt^1][j]*s[j])%mod*l[k])%mod;
				t[cnt][k]%=mod;
				t[cnt][k]+=((t[cnt^1][j]*l[j])%mod*s[k])%mod;
				t[cnt][k]%=mod;
			}
		}
	}
	int sum=0;
	for(int i=1;i<=n;i++)sum=(sum+t[cnt][i])%mod;
	cout<<sum;
	return 0;
}

对于 Medium 部分的做法:

我们不难发现上一步转移形式很固定,甚至正好是乘上某数再加起来,而且正好每次更新同时更新了所有值。

那么我们很容易想到用矩阵优化。

直接模版把上一步的内容放到矩阵中,快速幂即可。

由于个人习惯,代码中的 \(n\)\(m\) 与原题目不同。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int n,m,s[100005],l[100005],t[2][100005];
struct MT{
	int n,m,c[105][105];
	MT(){
		n=m=0;
		memset(c,0,sizeof(c));
	}
	MT friend operator*(MT a,MT b){
		MT c;
		c.n=a.n,c.m=b.m;
		for(int i=1;i<=a.n;i++){
			for(int j=1;j<=b.m;j++){
				for(int k=1;k<=a.m;k++){
					c.c[i][j]+=(a.c[i][k]*b.c[k][j])%mod;
					c.c[i][j]%=mod;
				}
			}
		}
		return c;
	}
}base,be;
void ksm(MT a,int b){
	while(b){
		if(b&1)be=be*a;
		a=a*a;
		b>>=1;
	}
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>s[i];
	for(int i=1;i<=n;i++)cin>>l[i];
	be.n=1,be.m=n;
	be.c[1][1]=1;
	base.n=base.m=n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			base.c[i][j]=((s[i]*s[j]%mod+s[i]*l[j]%mod)%mod+l[i]*s[j]%mod)%mod;
		}
	}
	ksm(base,m);
	int sum=0;
	for(int i=1;i<=n;i++)sum=(sum+be.c[1][i])%mod;
	cout<<sum;
	return 0;
}

对于 Hard 部分的做法:

这里应该才是最难的一步,可以直接将绿题变成紫题。

我们发现矩阵里如果存下所有点的数字,那么矩阵乘法做一次就超时了。

但是这 \(n\) 又非常大,也不好直接推导数学做法。

这时候我们想到在第一个点出来以后,我们走到湖中间上时去哪里只受到前面走了什么路的影响。

那么此时有多少种可能的做法就固定了。

走到下一个屋子,还要再回来,那么此时的点也确定了。

我们将这两步放到一起,从湖中间出发再走回来,在矩阵中记录上次走长路和短路的总方案数,这样就可以用矩阵快速求解了。

我们只需要先走到中间,再矩阵求方案数,再枚举回来的点,就可以得到答案了。

关于矩阵的具体推算可以看代码。

由于个人习惯,代码中的 \(n\)\(m\) 与原题目不同。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int n,m,s[100005],l[100005];
struct MT{
	int c[5][5],n,m;
	MT(){
		n=m=0;
		memset(c,0,sizeof(c));
	}
	MT friend operator*(MT a,MT b){
		MT c;
		c.n=a.n,c.m=b.m;
		for(int i=1;i<=a.n;i++){
			for(int j=1;j<=b.m;j++){
				for(int k=1;k<=a.m;k++)c.c[i][j]=(c.c[i][j]+(a.c[i][k]*b.c[k][j])%mod)%mod;
			}
		}
		return c;
	}
}base,be;
void ksm(MT a,int b){
	while(b){
		if(b&1)be=be*a;
		a=a*a;
		b>>=1;
	}
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>s[i];
	for(int i=1;i<=n;i++)cin>>l[i];
	be.n=1,be.m=2;
	be.c[1][1]=s[1],be.c[1][2]=l[1];
	base.n=base.m=2;
	int sum=0;
	for(int i=1;i<=n;i++)sum=(sum+((l[i]*s[i])%mod+(s[i]*s[i])%mod)%mod)%mod;
	base.c[1][1]=sum;
	sum=0;
	for(int i=1;i<=n;i++)sum=(sum+(s[i]*s[i])%mod)%mod;
	base.c[2][1]=sum;
	sum=0;
	for(int i=1;i<=n;i++)sum=(sum+((l[i]*s[i])%mod+(l[i]*l[i])%mod)%mod)%mod;
	base.c[1][2]=sum;
	sum=0;
	for(int i=1;i<=n;i++)sum=(sum+(s[i]*l[i])%mod)%mod;
	base.c[2][2]=sum;
	ksm(base,m-1);
	sum=0;
	for(int i=1;i<=n;i++){
		sum+=(s[i]*be.c[1][1])%mod;
		sum%=mod;
		sum+=(l[i]*be.c[1][1])%mod;
		sum%=mod;
		sum+=(s[i]*be.c[1][2])%mod;
		sum%=mod;
	}
	cout<<sum;
	return 0;
}
/*
s->s
s l s
s s s
l->s
l s s
s->l
s l l
s s l
l->l
l s l
*/


posted @ 2025-12-07 12:55  huhangqi  阅读(5)  评论(0)    收藏  举报