bzoj1875 [SDOI2009]HH去散步 偏移+化边+矩乘

来源:互联网 发布:python获取当前函数名 编辑:程序博客网 时间:2024/05/20 16:39

这个题一看便知是矩乘,但是有限制条件:不能往回走

其实正常的想法应该是记录来向然后容斥,但在减答案的时候是子问题递归的,所以容斥不行

然后可以记录路径终点的前继和起点的后继,但复杂度不科学

由于点与点之间子问题不是孤立的,所以类似区间dp的方法优化是不行的

这时要考虑限制每一次第一个连出,

而且题目要求的是边不能回,不是点不能回,

点不能回和便不能回的区别是:点有可能连多条边,边只会连两个点

所以对于点的讨论会记录一大堆状态,但对边的讨论只是两个点的讨论

因为边只连两个点,所以路径也就确定了,所以返回的可能路径也就只是唯一的了


转移的时候两步的情况是起始状态,所以倍增的时候步数是2 3 5 9 17

所以起始步数要-1;


码:

#include<iostream>#include<cstdio>#include<cstring>using namespace std;#define P 45989#define N 300int tot=-1,hou[N],xia[N],qian[N],shang[N],a,b,n,m,t,i,j,k,l,f[N][N],x,y,lin[N][N],lin2[N][N],ans;bool dyc=1;void jia(int a,int b){++tot;hou[tot]=xia[a];xia[a]=tot;qian[tot]=shang[b];shang[b]=tot;}int main(){memset(xia,-1,sizeof(xia));memset(shang,-1,sizeof(shang));scanf("%d%d%d%d%d",&n,&m,&t,&a,&b);a++;b++;for(i=1;i<=m;i++){scanf("%d%d",&x,&y);x++;y++;jia(x,y);jia(y,x);}for(i=0;i<=2*m;i++)f[i][i]=1;//a矩阵for(i=1;i<=n;i++)//b矩阵 {for(k=shang[i];k!=-1;k=qian[k]){for(j=xia[i];j!=-1;j=hou[j]){if(k==(j^1))continue;lin[k][j]=1;}    }}  t--;  while(t){if(t%2){if(dyc){dyc=0;for(i=0;i<=2*m;i++)//起点 for(j=0;j<=2*m;j++)//终点{f[i][j]=lin[i][j];f[i][j]%=P;} }else {for(i=0;i<=2*m;i++)//起点 for(k=0;k<=2*m;k++)//中间点{ for(j=0;j<=2*m;j++)//终点{lin2[i][j]+=f[i][k]*lin[k][j];lin2[i][j]%=P;}     }    for(i=0;i<=2*m;i++)    for(j=0;j<=2*m;j++)    f[i][j]=lin2[i][j],lin2[i][j]=0;}        }for(i=0;i<=2*m;i++)//起点 for(k=0;k<=2*m;k++)//中间点{ for(j=0;j<=2*m;j++)//终点{lin2[i][j]+=lin[i][k]*lin[k][j];lin2[i][j]%=P;}     }     t/=2;for(i=0;i<=2*m;i++)//起点 for(k=0;k<=2*m;k++)//中间点{ lin[i][k]=lin2[i][k];lin2[i][k]=0;        }}for(j=xia[a];j!=-1;j=hou[j])for(i=shang[b];i!=-1;i=qian[i]){ans+=f[j][i];ans%=P;}printf("%d",ans);}