fixed - 结论 - 计数 - dp

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

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

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

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

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

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

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

首先若k<n
考虑枚举的 gi g i 只能恰好包含 p p 的一个非空子集,枚举这个的大小t:,其会被计算 pt=1(pt) ∑ t = 1 p ( p t ) 这么多次。
我们希望其被计算1次,其实也很简单:给它配上一个容斥系数就可以了:
pt=1(pt)(1)t+1=1 ∑ t = 1 p ( p t ) ( − 1 ) t + 1 = 1 ,也就是说这个系数应该配到连通块个数上,即 gn g n 应当定义为,一个每个弱联通分量都是SCC的图,若其有 t t 个联通块,则对gn贡献是 (1)t+1 ( − 1 ) t + 1
考虑在配上系数后 k=n k = n 的情况,此时由于 i<n i < n 所以 t t 不能枚举到p的全集:t=1p1(pt)(1)t+1=(1)p+1
因而最终 k<n k < n 会被计算 11=0 1 − 1 = 0 次, k=n k = n 会被计算 1((1)p+1)=(1)p+1 1 − ( ( − 1 ) p + 1 ) = ( − 1 ) p + 1 次,发现正好就是 gn g n 的定义。
gn g n 推导出 fn f n 依然可以用最开始的式子,也就是与那个 1 − 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);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值