CQOI2018 Day1 社交网络

Cqoi2018 Day1 社交网络

题目背景:

当今社会,在社交网络上看朋友的消息已经成为许多人生活的一部分。通常,一个用户在社交网络上发布一条消息(例如微博、状态、Tweet等)后,他的好友们也可以看见这条消息,并可能转发。转发的消息还可以继续被人转发,进而扩散到整个社交网络中。

题目描述:

在一个实验性的小规模社交网络中我们发现,有时一条热门消息最终会被所有人转发。为了研究这一现象发生的过程,我们希望计算一条消息所有可能的转发途径有多少种。为了编程方便,我们将初始消息发送者编号为1,其他用户编号依次递增。该社交网络上的所有好友关系是已知的,也就是说对于\(A、B\)两个用户,我们知道\(A\)用户可以看到\(B\)用户发送的消息。注意可能存在单向的好友关系,即\(A\)能看到\(B\)的消息,但\(B\)不能看到\(A\)的消息。还有一个假设是,如果某用户看到他的多个好友转发了同一条消息,他只会选择从其中一个转发,最多转发一次消息。从不同好友的转发,被视为不同的情况。如果用箭头表示好友关系,下图展示了某个社交网络中消息转发的所有可能情况。这里写图片描述

输入输出个数:

输入格式:

输入文件第一行,为一个正整数\(n\),表示社交网络中的用户数:
第二行为一个正整数\(m\).表示社交网络中的好友关系数目。
接下来\(m\)行,每行为两个空格分隔的整数\(ai\)\(bi\),表示一组好友关系,即用户\(ai\)可以看到用户\(bi\)发送的消息。\(1≤n≤250,1≤ai,bi≤n,1≤m≤n(n-1)\)

输出格式:
输出文件共一行,为一条消息所有可能的转发途径的数量,除以\(10007\)所得的余数。

分析:

看到题目后发现这道题目就是一个裸的生成树计数的问题啊,然后我们就会想到这题用矩阵树来解决了。详情见:矩阵树。但是我并不会证明矩阵树,只能在这里大致的讲一讲题目的方法。大概就是构建一个度数矩阵\(D\)\(D[i][i]\)记录第i个点的入度,其余的为\(0\),然后再构建一个邻接矩阵\(A\)\(A[i][j]\)表示从\(i\)\(j\)是否存在一条边,存在就为1,否则为0。然后我们就可以得到矩阵树的关键矩阵:基尔霍夫矩阵\(C=D-A\)。然后我们在定义一个余子式\(M[i][j]\)表示A去掉第\(i\)行与第\(j\)列后的行列式。通过一系列的证明可以得出基尔霍夫矩阵任意一个余子式就是生成树的个数。然后这题就会变得非常简单了,构建出这个基尔霍夫矩阵,求出余子式的值就行了。最后不要忘记高斯消元的时候记得是在模意义下的就行了。具体的见代码。

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
const int maxn=255;
const int Md=10007;
ll D[maxn][maxn],C[maxn][maxn],A[maxn][maxn];
int n,m;
/*==================Define Area================*/
ll GetInv(ll x) {
	ll ans=1;
	ll y=Md-2;
	while(y) {
		if(y&1) ans*=x,ans%=Md;
		x*=x;
		x%=Md;
		y>>=1;
	}
	return ans;
}

int Gauss(ll A[255][255],int n) {
	int i,j,k,r;
	int f=1;
	for(i=1;i<=n;i++) {
		r=i;
		for(j=i+1;j<=n;j++) {
			if(A[j][i]>A[r][i]) r=j;
		}
		if(r!=i) {
			for(j=1;j<=n;j++) {
				swap(A[r][j],A[i][j]);
			}
			f*=-1;
		}
		for(k=i+1;k<=n;k++) {
			int f=1ll*A[k][i]*GetInv(A[i][i])%Md;//注意是在模意义下的
			for(j=i;j<=n;j++) {
				A[k][j]=((A[k][j]-1ll*f*A[i][j]%Md)%Md+Md)%Md;
			}
		}
	}
	int ans=1;
	for(int i=1;i<=n;i++) {
		ans*=A[i][i];
		ans%=Md;
	}
	ans=(f*ans%Md+Md)%Md;//最后求出余子式的值
	return ans%Md;
}  

int main() {
	read(n);read(m);
	for(int i=1;i<=m;i++) {
		int u,v;
		read(u);read(v);
		D[u][u]++;
		C[v][u]=1;
	}
	for(int i=1;i<n;i++) {
		for(int j=1;j<n;j++) {
			A[i][j]=D[i+1][j+1]-C[i+1][j+1];
		}
	}//构建出基尔霍夫矩阵
	ll res=Gauss(A,n-1);
	printf("%lld\n",res);
	return 0;
}
posted @ 2018-08-06 15:42  Apocrypha  阅读(255)  评论(0编辑  收藏  举报