fixed - 结论 - 计数 - dp

题目大意:给定 n , m ,问有多少 n n 列的矩阵 A ,满足:
1) i , j [ 1 , n ] , A i , j [ 0 , m )
2) i , j [ 1 , n ] , p , q 0 , ( A p ) i , j = ( A q ) i , j 。此时称 ( i , j ) 为矩阵 A 的不动点。
请注意, A 0 = I ,即 ( A 0 ) i , j ⇐⇒ [ i = j ] .
题解:
先把n=1判掉。
首先不要把它当成线性代数题,将其视为邻接矩阵。
那么就是要找到一对点,使得其两点间不同长度路径方案相同。
首先如果这个图不弱连通,那么一定存在一对点,永远到不了。
然后显然这个可以加强为不强联通。

然后过了一会,恩,也就是两个小时吧,意识到这个条件其实是充要条件(我会告诉你其间我还想到了什么exgcd之类的么)。

考虑反证,假设 ( x , y ) 是其不动点。
1)若 x y ,则 ( A p ) x , y = ( A 0 ) x , y = 0 ,但是因为其强联通,所以存在一个时刻 x 能到达 y
2)否则 x = y , ( A p ) x , y = ( A 0 ) x , x = 1 ,但这也是不可能的,因为其强联通并且 n 2 ,所以存在一个时刻, x 能走一个与其相连的点 z x 然后从 z 走到 x ;同时也可以一直走自环,因此方案数就至少是 2 了。

问题转化为 n 个点的有向强联通图计数,考虑主旋律的做法(顺便就当时主旋律的题解了吧)。显然自环不影响图的强联通性因此下文暂时不考虑。

先考虑一个有点问题的做法。
f n 表示答案, g n 表示若干个强联通分量(下文简记SCC)的方案数,也就是 g n = i = 1 n ( n 1 i 1 ) f i g n i
考虑计算 g n ,一个想法是,考虑所有不合g法的情况,缩点之后一定存在边。
钦定一些点是出度为0的点,然后剩下的点内部和从剩下的点到钦定的点间可以随便连,钦定的点就是g,钦定的点不能连向剩下的点,算出这个从总数中减去:
g n = h n i = 1 n 1 ( n i ) m i ( n i ) h n i g i
其中 h n 表示 n 个点的有向图

但这么算是肯定有问题的,但是我们想知道到底问题在哪里:

考虑真实情况是钦定的点有 k 个,缩点后有 p 个,会被计算几次。

首先若 k < n
考虑枚举的 g i 只能恰好包含 p 的一个非空子集,枚举这个的大小 t :,其会被计算 t = 1 p ( p t ) 这么多次。
我们希望其被计算1次,其实也很简单:给它配上一个容斥系数就可以了:
t = 1 p ( p t ) ( 1 ) t + 1 = 1 ,也就是说这个系数应该配到连通块个数上,即 g n 应当定义为,一个每个弱联通分量都是SCC的图,若其有 t 个联通块,则对 g n 贡献是 ( 1 ) t + 1
考虑在配上系数后 k = n 的情况,此时由于 i < n 所以 t 不能枚举到p的全集: t = 1 p 1 ( p t ) ( 1 ) t + 1 = ( 1 ) p + 1
因而最终 k < n 会被计算 1 1 = 0 次, k = n 会被计算 1 ( ( 1 ) p + 1 ) = ( 1 ) p + 1 次,发现正好就是 g n 的定义。
g n 推导出 f n 依然可以用最开始的式子,也就是与那个 1 的系数无关(因为本质上只不过是奇偶分开讨论而已)。
这样就做完了!开森。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<utility>
#define lint long long
#define p 1000000007
#define gc getchar()
#define mp make_pair
#define fir first
#define sec second
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define clr(a,n) memset(a,0,sizeof(int)*((n)+1))
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
#define N 3010
using namespace std;
typedef pair<int,int> pii;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
#define H(x) mi[(x)*((x)-1)]
int f[N],g[N],mi[N*N],c[N][N],h[N],fac[N],facinv[N];
inline int fast_pow(int x,int k,int ans=1) { for(;k;k>>=1,x=(lint)x*x%p) (k&1)?ans=(lint)ans*x%p:0;return ans; }
inline int prelude(int n,int m)
{
    fac[0]=1;rep(i,1,n) fac[i]=(lint)fac[i-1]*i%p;facinv[n]=fast_pow(fac[n],p-2),mi[0]=1;
    for(int i=n-1;i>=0;i--) facinv[i]=facinv[i+1]*(i+1ll)%p;rep(i,1,n*n) mi[i]=(lint)mi[i-1]*m%p;return 0;
}
int main()
{
    int n=inn(),m=inn();if(n==1&&m==2) return !printf("1\n");
    prelude(n,m),f[1]=g[1]=1;rep(i,1,n) h[i]=H(i);rep(i,0,n) c[i][0]=1;
    rep(i,1,n) rep(j,1,i) c[i][j]=c[i-1][j-1]+c[i-1][j],(c[i][j]>=p?c[i][j]-=p:0);
    rep(i,2,n)
    {
        g[i]=h[i];
        rep(j,1,i-1) g[i]-=(lint)c[i][j]*h[i-j]%p*g[j]%p*mi[j*(i-j)]%p,(g[i]<0?g[i]+=p:0);
        f[i]=g[i];rep(j,1,i-1) f[i]+=(lint)c[i-1][j-1]*f[j]%p*g[i-j]%p,(f[i]>=p?f[i]-=p:0);
    }
    return !printf("%lld\n",(lint)mi[n]*((h[n]-f[n]+p)%p)%p);
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/82561186