首页 > 其他分享 >HRC 004 T3 置换

HRC 004 T3 置换

时间:2024-10-14 15:33:09浏览次数:5  
标签:return tem int T3 long HRC 004 lcm dp

题目链接

前置知识

置换
轮换

\(60\space\text{pts}\) 解法

就像对于一个数,我们经常从素因子之积的角度看待它一样,在这道题中,我们从轮换的角度看待置换。
我们考虑一个轮换变成恒等变换所需次数:一个长度为 \(l\) 轮换,可以看做一个边数为 \(l\) 的环,置换乘法可以看做数字沿着边转一圈。假设某一条边的终点处的数字一开始为 \(k\),那么这条边的起点的位置编号一定是 \(k\)。以轮换 \((3,1,2)\) 为例,边 \(3\rightarrow1\)(位置编号)为例,起点的位置编号为 \(3\),终点一开始是 \(3\)。也就是说,一个数字从它开始的位置到达恒等变换的位置(位置编号也为这个数字),需要经过 \(l-1\) 条边,反映到幂次上,贡献为 \(l\)。
而对于整个置换,若变成恒等置换,则贡献为每个轮换长度的最小公倍数。答案即为每一种最小公倍数的平方与置换总数,即 \(n!\) 的商。
我们考虑统计出每一种最小公倍数的出现次数,设 \(dp_{i,j}\) 为将 \(i\) 个位置分配给若干个长度之最小公倍数为 \(j\) 的方案数。
先来考虑局部转移:将 \(dp_{i,j}\) 转移给 \(dp_{i+k\cdot s,\operatorname{lcm}(j,s)}\),即在已经分配的 \(i\) 个位置中再加入 \(k\) 个长度为 \(s\) 的轮换。对于位置的分配,可以转化为多重集 \(\{i\cdot a_1,s\cdot a_2, \cdots ,s\cdot a_{k+1}\}\) 的排列数,即 \(\frac{(i+k\cdot s)!}{{s!}^k i!}\)。而对于编号的分配,也就是置换环的边“交错情况”的分配,考虑第一个点有 \((n-1)\) 种终点,第二个有 \((n-2)\) 种,以此类推,单个置换环有 \((s-1)!\) 种,共 \(((s-1)!)^k\) 种。这一项与上一项合并得到 \(\frac{(i+k\cdot s)!}{s^k i!}\)。此外,由于每个置换环位置确定后内容也确定,需要消去几个置换环仅仅互换位置的情况,即除以 \(k!\),得到:

\[dp_{i+k\cdot s,\operatorname{lcm}(j,s)}=\sum{dp_{i,j}\frac{(i+k\cdot s)!}{s!^k i! k!}} \]

同时看到,在整体的转移过程中,得到 \(dp_{i,j}\) 时必有分子 \(i!\),这必将在下一步被消去,留下的 \(n!\) 也将在统计答案时因平均被消去,所以每次转移的系数被消为 \(\frac{1}{s!^k k!}\) ,这样最后不必再除 \(n!\)。

部分分代码
#include<bits/stdc++.h>
using namespace std;
unordered_map<long long,long long> dp[300];
unordered_map<long long,bool> vis[300];
long long n,p,t[300],tmaxn[300],inv[100100],invt[300];
long long gcd(long long a,long long b){
	long long tem=0;
	for(;a;){
		tem=a;
		a=b%a;
		b=tem;
	}
	return b;
}
long long lcm(long long a,long long b){
	return a*b/gcd(a,b);
}
long long qpow(long long x,long long y){
	if(!y)
	return 1;
	long long tem=qpow(x,y>>1);
	if(y&1){
		return x*tem%p*tem%p;
	}else{
		return tem*tem%p;
	}
}
long long finv(long long x){
	if(x<min(p,1ll*100100))
	return inv[x];
	else
	return qpow(x,p-2);
}
void dfs(long long x,long long y){
	if(!x){
		if(y>=100100)
		printf("%lld\n",y);
		return;
	}
	for(int i=1;i<=x;++i){
		dfs(x-i,lcm(y,i));
	}
}
int main(){
	freopen("perm.in","r",stdin);
	freopen("perm.out","w",stdout);
	scanf("%lld%lld",&n,&p);
	inv[0]=1;
	inv[1]=1;
	for(int i=2;i<min(p,1ll*100100);++i){
		inv[i]=(p-p/i)*inv[p%i]%p;
	}
	t[0]=1;
	for(int i=1;i<=n;++i){
		t[i]=t[i-1]*i%p;
	}
	invt[n]=qpow(t[n],p-2);
	for(int i=n-1;i;--i){
		invt[i]=invt[i+1]*(i+1)%p;
	}
	invt[0]=1;
	long long ans=0;
	dp[0][1]=1;
	vis[0][1]=1;
	for(int s=1;s<=n;++s){
		for(int i=n;i>=0;--i){
			for(int k=(n-i)/s;k;--k){
				for(auto j:vis[i]){
					if(!dp[i][j.first])
					continue;
					vis[i+k*s][lcm(j.first,s)]=1;
					long long tem=dp[i][j.first]*finv(qpow(s,k))%p*invt[k]%p;
					dp[i+k*s][lcm(j.first,s)]=(dp[i+k*s][lcm(j.first,s)]+tem)%p;
				}
			}
		}
	}
	for(auto i:vis[n]){
		ans=(ans+dp[n][i.first]*(i.first)%p*(i.first)%p)%p;
	}
	printf("%lld",ans);
	return 0;
}

正解

我们发现由于 \(\operatorname{lcm}\) 结果很多,上面做法会超时,考虑如果某个大于 \(\sqrt{n}\) 的因子在 \(\operatorname{lcm}\) 中出现的次数大于 \(1\) 则这个长度必定大于等于这个因子的平方,也大于 \(n\)。因此,每种这样的因子最多只会出现一次。我们把剩下的因子结合起来,将每种 \(\operatorname{lcm}\) 转换为这几个因子的 \(\operatorname{lcm}\) 复合上其它因子的出现情况。每次将前者作为状态,将后者相同的同时转移,枚举复合上的前者,正常转移后将 \(dp\) 的变化量乘上后者的平方(这样后面枚举的值会和前面复合),统计答案时再复合上前者平方即可。

正解
#include<bits/stdc++.h>
using namespace std;
const long long pr[6]={2,3,5,7,11,13};
struct node{
	int pos,lft;
	friend bool operator < (node x,node y){
		return x.lft<y.lft;
	}
}data[300];
long long n,p,base[3000],bcnt,t[300],dp[300][9000],up[300][9000],maxn[300],inv[100100],invt[300];
long long gcd(long long a,long long b){
	long long tem=0;
	for(;a;){
		tem=a;
		a=b%a;
		b=tem;
	}
	return b;
}
long long lcm(long long a,long long b){
	return a*b/gcd(a,b);
}
long long qpow(long long x,long long y){
	if(!y)
	return 1;
	long long tem=qpow(x,y>>1);
	if(y&1){
		return x*tem%p*tem%p;
	}else{
		return tem*tem%p;
	}
}
void dfs(int now,long long tim,long long sum){
	if(now==6){
		++bcnt;
		base[bcnt]=tim;
		return;
	}
	dfs(now+1,tim,sum);
	long long tem=1;
	for(;;){
		tem*=pr[now];
		if(sum+tem>n)
		break;
		dfs(now+1,tim*tem,sum+tem);
	}
}
int main(){
	freopen("perm.in","r",stdin);
	freopen("perm.out","w",stdout);
	scanf("%lld%lld",&n,&p);
	dfs(0,1,0);
	sort(base+1,base+bcnt+1);
	for(int i=1;i<=n;++i){
		data[i].pos=i;
		data[i].lft=i;
		for(int j=0;j<6;++j){
			for(;!(data[i].lft%pr[j]);){
				data[i].lft/=pr[j];
			}
		}
	}
	sort(data+1,data+n+1);
	inv[0]=1;
	inv[1]=1;
	for(int i=2;i<min(p,1ll*100100);++i){
		inv[i]=(p-p/i)*inv[p%i]%p;
	}
	long long ans=0;
	dp[0][1]=1;
	for(int lft=1,now=1;lft<=n;lft=now){
		for(int i=0;i<=n;++i){
			for(int j=1;j<=bcnt;++j){
				up[i][j]=dp[i][j];
			}
		}
		long long timr=data[lft].lft*data[lft].lft%p;
		for(;data[lft].lft==data[now].lft;++now){
			int s=data[now].pos;
			for(int j=bcnt;j;--j){
				int temlcm=lower_bound(base+1,base+bcnt+1,lcm(base[j],data[now].pos/data[now].lft))-base;
				if(temlcm>bcnt)
				continue;
				for(int i=n-s;i>=0;--i){
					if(!up[i][j])
					continue;
					int temi=i+s;
					long long div=inv[s];
					for(int k=1;temi<=n;++k,temi+=s,div=div*inv[s]%p*inv[k]%p){
						up[temi][temlcm]=(up[temi][temlcm]+up[i][j]*div)%p;
					}
				}
			}
		}
		for(int i=0;i<=n;++i){
			for(int j=1;j<=bcnt;++j){
				dp[i][j]=(dp[i][j]+timr*(up[i][j]-dp[i][j]))%p;
			}
		}
	}
	for(int i=1;i<=bcnt;++i){
		ans=(ans+dp[n][i]*base[i]%p*base[i])%p;
	}
	ans=(ans+p)%p;
	printf("%lld",ans);
	return 0;
}

标签:return,tem,int,T3,long,HRC,004,lcm,dp
From: https://www.cnblogs.com/blog21012004/p/18464317

相关文章

  • Nuxt3+PM2集群模式启动及勘误
    起因之前写过一篇Nuxt3的文章,Nuxt3环境变量配置,用到了PM2,但是里面的一些配置存在问题,最近有空又验证了一下,这里做一个勘误。问题PM2的启动配置中有一项是exec_mode,默认是fork,另一个可选值是cluster,fork是单进程模式,cluster是多进程模式,也就是常说的集群模式。最早开始......
  • P11157 【MX-X6-T3】さよならワンダーランド
    P11157【MX-X6-T3】さよならワンダーランド-洛谷|计算机科学教育新生态(luogu.com.cn)想复杂了,把需要的东西整理整理,就全出来了,列出适合的不等式后,可能就是个橙色。#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>#include<cmath>#de......
  • Splatt3R: Zero-shot Gaussian Splatting from Uncalibrated Image Pairs 论文解读
    目录一、概述二、相关工作1、近期工作2、DUSt3R3、MASt3R三、Splatt3R1、MASt3R的Backbone 2、高斯预测头3、点云与3D高斯参数结合4、3D高斯渲染5、损失函数四、实验 1、对比实验2、消融实验一、概述    该论文首次提出了一种无需任何相机参数和深......
  • Spring-AI基于GPT3.5实现开发WEB应用------Spring-AI框架
    packagecom.alatus.springai.controller;importjakarta.annotation.Resource;importorg.springframework.ai.chat.ChatResponse;importorg.springframework.ai.chat.prompt.Prompt;importorg.springframework.ai.openai.OpenAiChatOptions;importorg.springframe......
  • jsp高校外聘人员管理系统v9dt3
    jsp高校外聘人员管理系统v9dt3本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表项目功能部门信息,招聘计划,聘用人员,合同信息,员工,人事部,人员离职,员工薪资,工资发放,人员流动技术要求:   开发语言:JS......
  • CSP-J 2023 T3 一元二次方程 解题报告
    CSP-J2023T3一元二次方程解题报告Link前言今年\(CSP\)的原题,回家\(1h\)内写\(AC\),但是考场上没有写出来,原因是脑子太不好了,竟然调了两个小时没有调出来.一等奖悬那......正题看完题目,第一眼就是大模拟,并且\(CCF\)绝对不会让你好受,所以出了一个如此***钻的......
  • Windows-WMI 事件 ID 10或0x80041003——解决过程
    2024年10月8日国庆节后,第一天上班,实验室里一台PC机出现故障,Windows7系统,可以正常启动进入安全模式,但是正常启动无法加载桌面,可以看见鼠标,Ctrl+Alt+Del无法调出任务管理器。开始处理,进入安全模式,查看系统日志。发现一个错误如下(截取自[https://www.cnblogs.com/longware/p/78231......
  • FL Studio21.2.3.4004中文完整版,直接安装激活!免费且永久使用所有插件均可使用
    ......
  • IEC104规约的秘密之七----配置参数t1,t2,t3
    104通讯前需要配置通讯参数,一般有如下参数:IP地址,端口号,k,w,t1,t2,t3,公共地址,遥控超时参数,104主规约还有一个t0参数。本次只讲解t1,t2,t3这两个参数。这三个都是超时时间,t1用于两个地方,一个是发送的I帧没有得到及时的确认,在规约文本中有如下图:B站发送I(0,0)帧后,开始计时,A站回......
  • XYD1004CSPS
    T1序列[贪心,模拟]Description给定一个序列,每次可以将一个数减一,求让所有数字互不相同的最小操作数,\(0\)除外,也就是\(0\)可以相同。Solution贪心地,从大到小考虑每个数,都只需要减到第一个没有数出现的数。开个桶从大到小累计答案即可。T2XYD[构造]Description给定\(......