P2151 [SDOI2009] HH去散步 题解

传送门

简要题意:有n个人,m条无向边,走e条边,满足条件若第i条边为u>v则第i+1条边不能是v>u,问s>t的方案有多少个,取模45989。

因为要满足题目关于边的条件,所以我们考虑点边互换

uv的无向边一分为二变成u>v,v>u,第i条边记录两个变量为i.from,i.to,表示ii.fromi.to的边,他们互为反边

则从第i条边走到第j条边的条件是i.to=j.from

先考虑用动态规划思考转移方程:

fi,j表示第i个时刻走到第j条边的方案

fi,j=k=12mfi1,kak,j,其中若第k条边可以走到第j条边则ak,j=1,否则为0

因为e很大,所以我们要考虑矩阵乘法

构造转移矩阵B满足Bi,j表示第i条边到第j条边的方案。则Bi,j=1的情况为i.to=j.fromij不能互为反边

构造初始矩阵AAi,j表示第i条边到第j条边的方案。则Ai,j=1的条件时i.to=j.from=s,注意这里有个细节,因为我们是从s点而不是走某一条边开始走,所以我们不分正反边且只需枚举一个i.to=s的情况表示从s开始走即可。

Ansi,j表示最终从第i条边走到第j条边的方案数。如果i.to=sj.to=t,则ans+=Ansi,j

上代码:

#include<bits/stdc++.h>
#define ll long long
#define PLL pair<ll,ll>
using namespace std;
const ll M=130,mod=45989;
ll n,m,ti,s,t,flag;
ll u,v,idx,ans;
struct fu
{
ll from,to;
}bian[200];
struct jgt
{
ll a[M][M];
}A,B;
jgt operator * (const jgt t1,const jgt t2)
{
jgt t={0};
for(ll i=0;i<=idx;i++)
for(ll j=0;j<=idx;j++)
for(ll k=0;k<=idx;k++)
t.a[i][j]=(t.a[i][j]+(t1.a[i][k]*t2.a[k][j])%mod)%mod;
return t;
}
void sc(jgt t1)
{
printf("输出:\n");
for(ll i=0;i<=idx;i++)
{
for(ll j=0;j<=idx;j++)
printf("%lld ",t1.a[i][j]);
printf("\n");
}
printf("输出完毕!\n");
}
int main()
{
scanf("%lld %lld %lld %lld %lld",&n,&m,&ti,&s,&t);
for(ll i=1;i<=m;i++)
{
scanf("%lld %lld",&u,&v);
bian[idx++]={u,v};
bian[idx++]={v,u};
}
idx--,ti--;
for(ll i=0;i<=idx;i++)
for(ll j=0;j<=idx;j++)
if(bian[i].to==bian[j].from&&j!=i&&j!=(i^1)) B.a[i][j]=1;
for(ll i=0;i<=idx;i++)
{
for(ll j=0;j<=idx;j++)
if(bian[i].to==bian[j].from&&bian[j].from==s) A.a[i][j]=1;
if(bian[i].to==s)
{
flag=i;
break;
}
}
while(ti)
{
if(ti&1) A=A*B;
B=B*B;
ti=ti/2;
}
for(ll j=0;j<=idx;j++)
if(bian[j].to==t) ans=(ans+A.a[flag][j])%mod;
printf("%lld\n",ans);
return 0;
}
posted @   傻阙的缺  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示