RXD and numbers
Problem Description
RXD has a sequence
A1,A2,A3,…An
, which possesses the following properties:
- 1≤Ai≤m
- A1=An=1
- for all 1≤x≤m , there is at least one position p where Ap=x .
- for all x,y , the number of i(1≤i<n) which satisfies Ai=x and Ai+1=y is Dx,y .
One day, naughty boy DXR clear the sequence.
RXD wants to know, how many valid sequences are there.
Output the answer module 998244353 .
0≤Di,j<500,1≤m≤400
n≥2
- 1≤Ai≤m
- A1=An=1
- for all 1≤x≤m , there is at least one position p where Ap=x .
- for all x,y , the number of i(1≤i<n) which satisfies Ai=x and Ai+1=y is Dx,y .
One day, naughty boy DXR clear the sequence.
RXD wants to know, how many valid sequences are there.
Output the answer module 998244353 .
0≤Di,j<500,1≤m≤400
n≥2
Input
There are several test cases, please keep reading until EOF.
There are about 10 test cases, but only 1 of them satisfies m>50
For each test case, the first line consists of 1 integer m , which means the range of the numbers in sequence.
For the next m lines, in the i -th line, it consists of m integers, the j -th integer means Di,j .
We can easily conclude that n=1+∑mi=1∑mj=1Di,j .
There are about 10 test cases, but only 1 of them satisfies m>50
For each test case, the first line consists of 1 integer m , which means the range of the numbers in sequence.
For the next m lines, in the i -th line, it consists of m integers, the j -th integer means Di,j .
We can easily conclude that n=1+∑mi=1∑mj=1Di,j .
Output
For each test case, output "Case #x: y", which means the test case number and the answer.
Sample Input
2 1 2 2 1 4 1 0 0 2 0 3 0 1 2 1 0 0 0 0 3 1 4 0 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0
Sample Output
Case #1: 6 Case #2: 18 Case #3: 0
Source
题意:输入m,表示一共有m个结点,然后有一个m*m的矩阵,(i,j)表示i结点到j结点的边的数目。求以1号结点为根节点的有向图欧拉回路数目。
官方题解:
注意到计算的是欧拉回路. 把BEST's THEOREM 稍加修改可以得到答案. Trees×deg[1]!×∏i=2m(deg[i]−1)!∏i=1m∏j=1mDi,j!1 Trees表示1为根的生成树个数,用基尔霍夫矩阵计算就行了. 时间复杂度O(m3).
补充分析:
有d[i][j]为点i的度数,当i≠j时,d[i][j]=0;再假设矩阵A(G)为图G的邻接矩阵,在这个矩阵中,若i点和j点
可达,则有a[i][j]=1,否则a[i][j]为0。那么这个时候就可以令基尔霍夫矩阵为C(G)=D(G)-A(G)。然后通过求基尔霍
夫
矩阵的的行列值就可以了。由于要求以1号结点为根节点的生成树的个数,那么计算2~n结点的n-1阶基尔霍夫矩阵
的行列值就行了。Deg[1]表示1号结点的入度。
资料:
求有向图的欧拉回路个数,是BEST定理
ec(G)=ts(G)⋅deg(s)!⋅∏v∈V, v≠s(deg(v)−1)!, ts(G):=以s为根的外向树的个数
如何计算有向图的外向树个数,这要用到MatrixTree定理
基尔霍夫矩阵K=度数矩阵D−邻接矩阵A
无向图的度数矩阵就是每个点自己的度数
有向图的度数矩阵就是每个点自己的入度
邻接矩阵是表示u−>v边的个数的矩阵
重边:按照边数计算,自环:不计入度数
无向图生成树计数:c=|K的任意1个n−1阶主子式|
有向图外向树计数:c=|去掉根所在的那阶得到的主子式|
以上都是copyTaosama大佬的博客的
最后的话,推出题解的公式,套上行列式求值模板和除法逆元模板就可以了。另外,这道题貌似跟CSU1805解法
相似。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int mod = 998244353;
const int maxn = 1e6;
int Mat[405][405];
int tempa[405][405];
int tempb[405][405];
int out[405];
int in[405];
long long inv[maxn];
bool dance;
///行列式求值模板
int solve(int A[405][405],int n){
int ans=1;
dance=0;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
int x=i;int y=j;
while(A[y][i]){
long long t=A[x][i]/A[y][i];
for(int k=1;k<=n;k++)
A[x][k]=(A[x][k]-t*A[y][k])%mod;
swap(x,y);
}
if(x!=i){
dance^=1;
for(int k=1;k<=n;k++)swap(A[i][k],A[x][k]);
}
}
ans*=A[i][i];
if(ans==0){
return 0;
}
ans=ans%mod;
}
if(dance){
ans=ans*(-1);
}
ans=ans%mod;
ans+=mod;
ans=ans%mod;
return ans;
}
//行列式求值模板
long long fact(long long n){
long long ans=1;
for(int i=1;i<=n;i++){
ans*=i;
ans%=mod;
}
return ans;
}
long long exgcd(long long a,long long b,long long &x,long long &y){//非阶乘的除法逆元
if(b==0)
{
x=1;
y=0;
return a;
}
long long d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
/*long long inv(long long a){//非阶乘的除法逆元
long long x,y;
exgcd(a,mod,x,y);
return (x%mod+mod)%mod;
}*/
void init(){
inv[1]=1;
for(int i=2;i<maxn; i++){
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
inv[0]=1;
for(int i=1;i<maxn; i++) {
inv[i]=inv[i]*inv[i-1]%mod;
}
}
int main(){
init();//阶乘除法逆元
int n;
int cas=1;
while(~scanf("%d",&n)){
int ans[405][405];
int a[405][405];
memset(Mat,0,sizeof(Mat));
memset(tempa,0,sizeof(tempa));
memset(tempb,0,sizeof(tempb));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
scanf("%d",&a[i][j]);
tempb[i][j]+=a[i][j];
in[j]+=a[i][j];
out[i]+=a[i][j];
}
}
int ok=1;
for(int i=0;i<n;i++){
tempa[i][i]=in[i];
if(in[i]!=out[i]){
ok=0;
break;
}
}
if(!ok){
printf("Case #%d: %lld\n",cas++,0);
continue;
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
Mat[i][j]=tempa[i][j]-tempb[i][j];
}
}
for(int i=1;i<n;i++){
for(int j=1;j<n;j++){
ans[i][j]=Mat[i][j];
}
}
long long sum=solve(ans,n-1);
sum=sum*fact(in[0])%mod;
for(int i=1;i<n;i++){
sum=sum*fact(in[i]-1);
sum%=mod;
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
sum=sum*inv[a[i][j]]%mod;
}
}
printf("Case #%d: %lld\n",cas++,sum);
}
return 0;
}