题目:H-Permutation Counting_MINIEYE杯第十六届华中科技大学程序设计邀请赛 (nowcoder.com)
思路:
- 首先啊,先把题目读清楚, 给一对,(X,Y), 要满足Py>Px, 然后p(permutation )是一个1到n,且各个元素不同的数列
- 问这个P啊有多少种排列方式
- 首先 Py>Px , 代表着 位置 y是一定要大于 位置 x的, 利用这个可以建一个有向图,连接一个y到x的有向边
- 什么时候符合条件? 就是没有环的时候符合条件
- 其次 说有对(x,y) x都是不相同的, 相当于,每一个点最多只有1个父亲,于是这个性质就把图变成了树图(有向图,没有环,且父亲最多只有1个)
- 注意这个树图是森林,没有要求是1个树.
- 那么就转化为了,数的节点贡献问题, 该题关键就是就是他的size大小, 会拿size个数给他排大小去,对于他一定是比儿子大,他们之间的顺序就是固定的
- 但是儿子之间的顺序没有固定,就变成了选球放盒子问题(每一个球不同),的一共种类数, 树与树之间也是如此 利用组合数解决
- 具体看代码
#include <bits/stdc++.h> using namespace std; #define ri register int #define M 2000006 vector <int> p[M]; vector <int> q; int n,m; int vis[M]; int sz[M]; void dfs1(int a) { vis[a]=1; sz[a]++; for(ri i=0;i<p[a].size();i++) { int b=p[a][i]; dfs1(b); sz[a]+=sz[b]; } return ; } long long ans=1; const int mod=998244353; int in[M]; long long arr[M],inv[M]; long long ksn(long long a,int n) { long long ans=1; while(n) { if(n&1) ans=ans*a%mod; n>>=1;a=a*a%mod; } return ans; } void init(){ arr[0]=1;inv[0]=1; for(ri i=1;i<=n;i++) { arr[i]=arr[i-1]*i%mod; inv[i]=ksn(arr[i],mod-2); } } long long zh(int a,int b) { if(a<b) return 0; if(a==b||b==0) return 1; return arr[a]*inv[a-b]%mod*inv[b]%mod; } void dfs2(int a) { int tmp=sz[a]-1; for(ri i=0;i<p[a].size();i++) { int b=p[a][i]; ans=(ans*zh(tmp,sz[b]))%mod; tmp-=sz[b]; dfs2(b); } } int main(){ ios::sync_with_stdio(false); cin.tie(0); cin>>n>>m; init(); for(ri i=1;i<=m;i++) { int a,b; cin>>a>>b; p[b].push_back(a); in[a]++; } for(ri i=1;i<=n;i++) { if(in[i]==0) { q.push_back(i); } } for(ri i=0;i<q.size();i++) { int b=q[i]; dfs1(b); } for(ri i=1;i<=n;i++) { if(vis[i]==0) { printf("0\n"); return 0; } } int tmp=n; for(ri i=0;i<q.size();i++) { int a=q[i]; ans=(ans*zh(tmp,sz[a]))%mod; dfs2(a); tmp-=sz[a]; } cout<<ans; return 0; }
后记:
- 也是通过2点之间的关系,从而连边,然后就可以建图