Codeforces Round #114 (Div. 1) E Wizards and Bets
题目链接:Wizards and Bets
这场CF的D题感觉读不懂,而且过题人数比E题少很多,所以就看的E题了。
题意:一个有向图中,没有出度的边称为 sink,没有入度的边称为 source,且保证其数目相等,并对其按照大小排序分别得到了新的顺序。现在要求 source 中的点与 sink 中的点一一对应,且路径中不存在相交的地方,对于 source 中的两个点 (i, j) ,对应 sink 中的点分别为 (x1, x2),若是序列的逆序数为偶数则得分为 +1,否则为 -1。问所有的不存在相交的边的情况中,可以得到的总分是多少。
题解:刚刚考完研,居然就忘了行列式与逆序数之间的关系。对于相交的边,总存在与其对称的情况把其抵消掉,所以这个情况可以不用考虑,直接计算总的情况即可。定义矩阵 matrix[i][j] 为 source 中的点 i 到 sink 中的点 j 的不同路径数,然后对求这个矩阵的行列式即可。
其中计算行列式为高斯消元,然后对角线元素相乘,抄了个板子贴了上来。
#include <bits/stdc++.h> using namespace std; typedef long long LL; vector<int> g[1005]; LL n,m,p; LL in[1005],out[1005],pos1[1005],pos2[1005],cnt1,cnt2; LL matrix[1005][1005],num[1005][1005]; LL vis[1005]; LL sink[1005],sink_num,source[1005],source_num; void dfs(LL u){ vis[u]=1; if(out[u]==0){ num[u][u]=1; } for(LL i=0;i<g[u].size();i++){ LL v=g[u][i]; if(!vis[v]) dfs(v); for(LL j=0;j<sink_num;j++){ num[u][sink[j]]=(num[v][sink[j]]+num[u][sink[j]])%p; } } } void solve(LL n){ LL ans=1,flag=0; for(LL i=0;i<n;i++){ for(LL j=i+1;j<n;j++){ LL x=i,y=j; while(matrix[y][i]){ LL t=matrix[x][i]/matrix[y][i]; for(LL k=i;k<n;k++) matrix[x][k]=(matrix[x][k]-matrix[y][k]*t)%p; swap(x,y); } if(x!=i){ for(LL k=0;k<n;k++) swap(matrix[i][k],matrix[x][k]); flag^=1; } } if(matrix[i][i]==0){ printf("%d\n",0); return ; } else ans=ans*matrix[i][i]%p; } if(flag!=0) ans*=-1; if(ans<0) ans+=p; printf("%lld\n",ans); } int main(){ scanf("%lld%lld%lld",&n,&m,&p); for(LL i=1;i<=m;i++){ LL u,v; scanf("%lld%lld",&u,&v); out[u-1]++; in[v-1]++; g[u-1].push_back(v-1); } for(LL i=0;i<n;i++){ if(in[i]==0){ pos1[i]=cnt1++; source[source_num++]=i; } if(out[i]==0){ pos2[i]=cnt2++; sink[sink_num++]=i; } } for(LL i=0;i<n;i++) if(in[i]==0) dfs(i); for(LL i=0;i<cnt1;i++){ for(LL j=0;j<cnt2;j++){ matrix[i][j]=num[source[i]][sink[j]]; } } solve(cnt1); return 0; }