题目大意:给定
,问有多少
行
列的矩阵
,满足:
1)
;
2)
。此时称
为矩阵
的不动点。
请注意,
,即
.
题解:
先把n=1判掉。
首先不要把它当成线性代数题,将其视为邻接矩阵。
那么就是要找到一对点,使得其两点间不同长度路径方案相同。
首先如果这个图不弱连通,那么一定存在一对点,永远到不了。
然后显然这个可以加强为不强联通。
然后过了一会,恩,也就是两个小时吧,意识到这个条件其实是充要条件(我会告诉你其间我还想到了什么exgcd之类的么)。
考虑反证,假设
是其不动点。
1)若
,则
,但是因为其强联通,所以存在一个时刻
能到达
。
2)否则
,但这也是不可能的,因为其强联通并且
,所以存在一个时刻,
能走一个与其相连的点
然后从
走到
;同时也可以一直走自环,因此方案数就至少是
了。
问题转化为 个点的有向强联通图计数,考虑主旋律的做法(顺便就当时主旋律的题解了吧)。显然自环不影响图的强联通性因此下文暂时不考虑。
先考虑一个有点问题的做法。
令
表示答案,
表示若干个强联通分量(下文简记SCC)的方案数,也就是
;
考虑计算
,一个想法是,考虑所有不合g法的情况,缩点之后一定存在边。
钦定一些点是出度为0的点,然后剩下的点内部和从剩下的点到钦定的点间可以随便连,钦定的点就是g,钦定的点不能连向剩下的点,算出这个从总数中减去:
其中
表示
个点的有向图
但这么算是肯定有问题的,但是我们想知道到底问题在哪里:
考虑真实情况是钦定的点有 个,缩点后有 个,会被计算几次。
首先若
。
考虑枚举的
只能恰好包含
的一个非空子集,枚举这个的大小
:,其会被计算
这么多次。
我们希望其被计算1次,其实也很简单:给它配上一个容斥系数就可以了:
,也就是说这个系数应该配到连通块个数上,即
应当定义为,一个每个弱联通分量都是SCC的图,若其有
个联通块,则对
贡献是
。
考虑在配上系数后
的情况,此时由于
所以
不能枚举到p的全集:
。
因而最终
会被计算
次,
会被计算
次,发现正好就是
的定义。
用
推导出
依然可以用最开始的式子,也就是与那个
的系数无关(因为本质上只不过是奇偶分开讨论而已)。
这样就做完了!开森。
#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);
}