Jzoj5418 合影

志愿者合影时,部分人有特定位置要求。通过构建树形结构表示要求,判断无环后,使用深度优先搜索计算合法排列方案数模质数的余数。涉及树形动态规划和组合数学知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目背景
       热烈庆祝北京师范大学附属实验中学成立100周年!
问题描述
       经过一天的忙碌,志愿者们结束了他们的工作,准备站在一排合影留念。

       现在总共有n名志愿者留下来准备合影。不过,进程并不是那么顺利,有些同学提出了一些奇奇怪怪的要求(每个人最多只会提出一个):他必须站在另外一个同学的左边(不一定相邻),仁慈的老师满足了他们的要求。这时,其中一位来自11班的同学小Z陷入了沉思:总共有多少种不同的合法方案数呢?(两种方案不同当且仅存在至少一名同学他在这两个方案当中站的位置不同。)小Z很快就算出来了,于是就把自己的这个问题告诉了好朋友小C。不过,由于小C的数学功底不足,小Z只要求他算出这个答案模质数p的余数就可以了。可就算这样,小C也不会做。为了显示自己的水平很高(实际上很低),他找到了你,并把你得出的答案报给小Z,所以你可一定要算对啊!

是一个组合数学的问题(而且我做过改了题面的版本,但是想不起来了!)

我们将所有的要求变成一颗树,若a要求在b前面,那么a<-b连一条边,若有x没有要求,连一条0->x的边

先判断有没有环,有就直接输出0

没有环,就从0开始dfs,当每个子树的方案都已经求出来后就可以用乘法原理合并了

其实就是每次将一堆节点插入到另一堆中,在乘上子树自己的方案数

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<vector> 
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 200010 
#define L long long  
using namespace std;
L js[N],inv[N]; vector<int> s[N]; 
int f[N],sz[N],n,m,M,T; bool in[N],v[N];
inline L C(int n,int m){ return js[n]*inv[m]%M*inv[n-m]%M; }
inline L pow(L x,L k,L s=1){
	for(;k;x=x*x%M,k>>=1) if(k&1) s=s*x%M; return s;
}
inline bool dfs(int x){
	if(in[x]) return 1;
	if(v[x]) return 0;
	in[x]=v[x]=1;
	if(dfs(f[x])) return 1;
	in[x]=0; return 0;
}
inline L gAns(int x){
	L A=1; 
	for(int i=0,z=s[x].size(),v;i<z;++i){
		A=A*gAns(v=s[x][i])%M;
		sz[x]+=sz[v]; A=A*C(sz[x],sz[v])%M; 
	}
	++sz[x]; return A;
}
int _extend(){
	scanf("%d%d%d",&n,&m,&M); s[0].clear();
	memset(in,0,sizeof in); memset(v,0,sizeof v);
	memset(sz,0,sizeof sz); memset(f,0,sizeof f);
	for(int i=*js=*inv=1;i<=n;++i) js[i]=js[i-1]*i%M,s[i].clear(); 
	inv[n]=pow(js[n],M-2); for(int i=n;i;--i) inv[i-1]=inv[i]*i%M;
	for(int x,y;m--;s[f[x]=y].push_back(x)) scanf("%d%d",&x,&y); 
	for(int i=1;i<=n;++i) if(!f[i]) s[0].push_back(i); v[0]=1; 
	for(int i=1;i<=n;++i) if(!v[i] && dfs(i)) return 0&puts("0");
	printf("%lld\n",gAns(0));
}
int main(){
	freopen("photo.in","r",stdin);
	freopen("photo.out","w",stdout);
	for(scanf("%d",&T);T--;_extend());
}

### 关于本通 55.1 合影效果的实现方法 #### 方法概述 该问题的核心在于对男性和女性分别进行排序处理,其中男性按身高从小到大排列,而女性则按身高从大到小排列。最终将两组数据拼接在起形成结果。 以下是基于C++的种具体实现方式: ```cpp #include <iostream> #include <algorithm> using namespace std; int main() { int n; cin >> n; // 定义存储数组 double maleHeights[40]; double femaleHeights[40]; int mCount = 0, fCount = 0; // 输入性别与身高,分类存储 for (int i = 0; i < n; ++i) { string gender; double height; cin >> gender >> height; if (gender == "male") { maleHeights[mCount++] = height; } else if (gender == "female") { femaleHeights[fCount++] = height; } } // 对男性身高升序排序 sort(maleHeights, maleHeights + mCount); // 对女性身高降序排序 sort(femaleHeights, femaleHeights + fCount); reverse(femaleHeights, femaleHeights + fCount); // 输出结果 for (int i = 0; i < mCount; ++i) { printf("%.2f ", maleHeights[i]); } for (int i = 0; i < fCount; ++i) { printf("%.2f ", femaleHeights[i]); } return 0; } ``` 此代码实现了题目中的需求[^1],通过`sort()`函数完成排序操作,利用`reverse()`来调整女性身高的顺序[^2]。最后输出时确保每位数值均保留两位小数[^3]。 另外种思路可以采用结构体保存每个人的信息(包括性别与身高),再依据条件筛选排序[^4]。这种方式虽然稍显复杂,但在涉及更多属性的情况下更为灵活。 对于Java版本,同样遵循类似的逻辑流程,只是语法有所区别[^5]。 ### 注意事项 - 数据读取过程中应严格区分性别字段。 - 排序方向需准确无误,即男性由低至高,女性反之。 - 所有输出值的小数部分统控制为两位。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值