Luogu P4321 随机漫游

期望DP要倒着推

Luogu P4321


题意

LOJ #2542

不一定是树,询问点不一定均为1


$Solution$ 

设计一个巧妙的DP状态

设$ F(S,x)$表示当前在点$ x$已经走遍了$ S$,走完剩下所有点的期望步数

这样推转移$ DP$的时候一定是从$ F(S|y,y)$转移过来

容易发现$ S|y$->$S$是不可能会变大的,即这维不可能成环

因此从大到小枚举$ S$,对当前$ S$,显然比$ S$大的状态已经被计算,暴力$ n^3$高斯消元消出这维就好了

时间复杂度$ O(2·n·n^3)$

为什么不直接$ Min-Max$容斥呢

随机游走

每次枚举$ S$消出当前$ Min(S,x)$中的x这维

复杂度不变

而且很好写啊...


$ my \ code$

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define p 998244353
#define rt register int
#define ll long long
using namespace std;
inline ll read(){
    ll x=0;char zf=1;char ch=getchar();
    while(ch!='-'&&!isdigit(ch))ch=getchar();
    if(ch=='-')zf=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('\n');}
int k,m,n,x,y,z,cnt,S;
bool link[20][20];int a[20][20],d[20];
int inv[20],ans[20][1<<18];
int ksm(int x,int y=p-2){
    int ans=1;
    for(rt i=y;i;i>>=1,x=1ll*x*x%p)if(i&1)ans=1ll*ans*x%p;
    return ans;
}
void gauss(){
    for(rt i=1;i<=n;i++)if(!(S>>i-1&1)){
        for(rt j=i;j<=n;j++)if(a[j][i]){
            if(j!=i)for(rt k=1;k<=n+1;k++)swap(a[i][k],a[j][k]);
            break;
        }
        if(!a[i][i])continue;
        inv[i]=ksm(a[i][i]);
        for(rt j=i+1;j<=n;j++)if(a[j][i]){
            const int x=1ll*a[j][i]*inv[i]%p;
            for(rt k=i;k<=n+1;k++)if(a[i][k])(a[j][k]-=1ll*a[i][k]*x%p)%=p;
        }
    }
    for(rt i=n;i>=1;i--)if(!(S>>i-1&1)){
        ans[i][S]=1ll*a[i][n+1]*inv[i]%p;
        for(rt j=i-1;j>=1;j--)if(a[j][i])(a[j][n+1]-=1ll*ans[i][S]*a[j][i]%p)%=p;
    }
}
int ret[20][1<<18];
int main(){
    n=read();m=read();
    for(rt i=1;i<=m;i++){
        x=read();y=read();d[x]++;d[y]++;
        link[x][y]=link[y][x]=1;
    }
    //Max(S)走遍集合S的时间
    //Min(S)第一次走到S的时间 
    for(S=1;S<(1<<n);S++){
        memset(a,0,sizeof(a));
        for(rt i=1;i<=n;i++){
            if(S>>i-1&1)a[i][i]=1;
            else {
                for(rt j=1;j<=n;j++)if(link[i][j]&&!(S>>j-1&1))a[i][j]=-1;
                a[i][i]=d[i];a[i][n+1]+=d[i];
            }
        }
        gauss();
    }
    //Max(S)=sigma Min(T) (-1)|T|+1 
    for(rt i=1;i<=n;i++){
        for(rt j=0;j<(1<<n);j++)if(!(__builtin_popcount(j)&1))ans[i][j]*=-1;
        for(rt j=0;j<n;j++)
        for(rt k=0;k<(1<<n);k++)if(k>>j&1)(ans[i][k]+=ans[i][k^(1<<j)])%=p; 
    }
    for(rt T=read();T;T--){
        n=read();S=0;
        for(rt i=1;i<=n;i++)S|=(1<<read()-1);
        x=read();writeln((ans[x][S]+p)%p);
    }
    return 0;
}

 

posted @ 2019-01-04 11:20  Kananix  阅读(182)  评论(0编辑  收藏  举报

Contact with me