bzoj1875 [SDOI2009]HH去散步

bzoj1875 [SDOI2009]HH去散步


原题链接


题解

如果没有边方面的限制这就是道矩乘大水题。。。
然而有
所以考虑边->点,统计一条边走向另一条边的方案。
把无向边拆成两条有向边,枚举点,再枚举终点是他的边,再枚举起点是他的边。。。
就可以了,再把矩阵变成(t-1)次方,枚举起点的边终点的边统计答案。


Code

// It is made by XZZ
#include<cstdio>
#include<algorithm>
#define Fname "bzoj1875"
using namespace std;
#define rep(a,b,c) for(rg int a=b;a<=c;a++)
#define drep(a,b,c) for(rg int a=b;a>=c;a--)
#define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
#define il inline
#define rg register
#define vd void
#define mod 45989
typedef long long ll;
il ll gi(){
    rg ll x=0;rg char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}
int n,m;
struct Mat{
    ll s[122][122];
    Mat(){rep(i,1,m)rep(j,1,m)s[i][j]=0;}
};
il vd Add(ll&a,ll b){a=(a+b)%mod;}
Mat operator +(Mat a,Mat b){
    rep(i,1,m)rep(j,1,m)Add(a.s[i][j],b.s[i][j]);
    return a;
}
Mat operator *(Mat a,Mat b){
    Mat ret;
    rep(i,1,m)rep(j,1,m)rep(l,1,m)Add(ret.s[i][j],a.s[i][l]*b.s[l][j]%mod);
    return ret;
}
vd operator +=(Mat&a,Mat b){a=a+b;}
vd operator *=(Mat&a,Mat b){a=a*b;}
Mat operator ^(Mat a,ll b){
    Mat ret=a;--b;
    while(b){
    if(b&1)ret*=a;
    a*=a,b>>=1;
    }return ret;
}
vd operator ^=(Mat&a,ll b){a=a^b;}
namespace e{
    int id=0,fir[51],nxt[121],dis[121];
    il vd add(int a,int b){nxt[++id]=fir[a],fir[a]=id,dis[id]=b;}
}
#define rev(o) ((o)&1?(o)+1:(o)-1)
int main(){
    // freopen(Fname".in","r",stdin);
    // freopen(Fname".out","w",stdout);
    n=gi(),m=gi();
    ll t=gi();
    int S=gi()+1,T=gi()+1,a,b;
    using namespace e;
    rep(i,1,m)a=gi()+1,b=gi()+1,add(a,b),add(b,a);
    m<<=1;
    Mat s;
    rep(i,1,n)erep(x,i)erep(y,i)if(x!=y)s.s[rev(y)][x]=s.s[rev(x)][y]=1;
    s^=t-1;
    ll ans=0;
    erep(i,S)erep(j,T)Add(ans,s.s[i][rev(j)]);
    printf("%lld
",ans);
    return 0;
}
/*
4 5 3 0 0
0 1
0 2
0 3
2 1
3 2
*/

PS.常数优化真TM逼了狗。。。把ll mod=45989改成#define mod 45989就50->100
原文地址:https://www.cnblogs.com/xzz_233/p/7503331.html