首页 > 其他分享 >「解题报告」AHOI2022 排列

「解题报告」AHOI2022 排列

时间:2023-02-18 18:25:59浏览次数:52  
标签:排列 int AHOI2022 break 解题 题解 cycc

这个标题格式我才看出来它的优越性,如果用「AHOI2022 排列 题解」这种写法会感觉「AHOI2022」「排列」「题解」是并列地位的,比较奇怪,我目前就想到这一种解决方案,也就是 APJ 的那种标题格式 .


首先 \(f(i,j)\neq 0\) 当且仅当 \(a_i,a_j\) 在同一个置换环中 .

然后 \(v(p)\) 就是 \(p\) 所有置换环长度的 lcm,交换 \(a_i,a_j\) 相当于合并两个置换环,可以手玩一下看出来 .

对于环长 \(\{c_k\}\) 满足关系 \(\displaystyle\sum_{i=1}^kc_i\le n\),于是本质不同的 \(c_i\) 只有 \(O(\sqrt n)\) 种,可以暴力枚举两种 \(c_i\) .

首先处理初始答案,然后每次合并 \(c_i,c_j\) 对 \(c_i,c_j,c_i+c_j\) 分解质因数然后重新计算贡献即可 .

时间复杂度 \(\Theta(n\log n)\) .

const int N = 5e5 + 123, P = 1e9 + 7;
int n, p[N], inv[N], cc[N];
bool nsp[N], vis[N];
vector<pii> fac[N];
multiset<int> cycc[N];
vector<int> prime, cyc;
inline void init(int n)
{
	inv[1] = 1;
	for (int i=2; i<=n; i++) inv[i] = 1ll * (P - P/i) * inv[P % i] % P;
	for (int i=2; i<=n; i++)
	{
		if (nsp[i]) continue;
		prime.emplace_back(i);
		for (int j=i; j<=n; j+=i){nsp[j] = true; fac[j].emplace_back(make_pair(i, i));}
		for (int j=i; j<=n/i; j*=i)
			for (int k=j*i; k<=n; k+=j*i){fac[k].pop_back(); fac[k].emplace_back(make_pair(i, j * i));}
	}
}
int main()
{
	int T; scanf("%d", &T); init(5e5);
	while (T--)
	{
		scanf("%d", &n); int ans = 0, all = 1; cyc.clear(); cyc.emplace_back(-1); 
		for (int x : prime)
		{
			if (x > n) break;
			cycc[x].clear();
		}
		for (int i=1; i<=n; i++){vis[i] = false; cc[i] = 0;} //
		for (int i=1; i<=n; i++) scanf("%d", p+i);
		for (int i=1; i<=n; i++)
			if (!vis[i])
			{
				int cycle = 0;
				for (int j=i; !vis[j]; j=p[j]){++cycle; vis[j] = true;}
				++cc[cycle]; 
			}
		for (int i=1; i<=n; i++)
			if (cc[i])
			{
				cyc.emplace_back(i);
				for (int _=1; _<=cc[i]; _++)
					for (pii e : fac[i]) cycc[e.first].insert(e.second);
			}
		for (int x : prime)
		{
			if (x > n) break;
			cycc[x].insert(1);
		}
		for (int x : prime)
		{
			if (x > n) break;
			all = 1ll * all * *cycc[x].rbegin() % P;
		}
		int m = cyc.size() - 1;
		for (int i=1; i<=m; i++)
			for (int j=i; j<=m; j++)
			{
				if ((i == j) && (cc[cyc[i]] == 1)) continue;
				int now = all, val;
				if (i == j) val = 1ll * cc[cyc[i]] * (cc[cyc[i]] - 1) % P;
				else val = 2ll * cc[cyc[i]] * cc[cyc[j]] % P;
				auto add = [&](int c, int v)
				{
					for (pii d : fac[c])
					{
						int p = d.first, e = d.second;
						now = 1ll * now * inv[*cycc[p].rbegin()] % P;
						if (v == 1) cycc[p].insert(e);
						else cycc[p].erase(cycc[p].find(e));
						now = 1ll * now * *cycc[p].rbegin() % P;
					}
				};
				add(cyc[i] + cyc[j], 1); add(cyc[i], -1); add(cyc[j], -1);
				(ans += 1ll * cyc[i] * cyc[j] % P * val % P * now % P) %= P;
				add(cyc[i] + cyc[j], -1); add(cyc[i], 1); add(cyc[j], 1);
			}
		printf("%d\n", ans);
	}
	return 0;
}

代码大概是仿的 RSY 的 .

标签:排列,int,AHOI2022,break,解题,题解,cycc
From: https://www.cnblogs.com/CDOI-24374/p/17133196.html

相关文章

  • 排列组合的知识
    排列组合公式 排列组合方法一、计数按照统计要求,将符合所有条件的结果筛选出来,统计所有结果的数量叫做计数!二、分类加法完成一件事的方法,有n类方案,第一类方案中有......
  • 排列与组合
    排列的定义:从n个不同元素中,任取m(m≤n,m与n均为自然数)个不同的元素按照一定的顺序排成一列,叫做从n个不同元素中取出m个元素的一个排列;从n个不同元素中取出m(m≤n)个元素......
  • 全排列
    #include<iostream>#include<cstdio>usingnamespacestd;//输出全排列constintMAX=1000;inta[MAX];intn;intbook[MAX];voiddfs(intstep){inti;if(step......
  • 字符串的排列
    字符串的排列给你两个字符串 s1 和 s2,写一个函数来判断s2是否包含s1 的排列。如果是,返回true;否则,返回false。换句话说,s1的排列之一是s2的子串。示例......
  • B2066 救援--解题思路
    救援救援题目描述救生船从大本营出发,营救若干屋顶上的人回到大本营,屋顶数目以及每个屋顶的坐标。和人数都将由输入决定,求出所有人都到达大本营并登陆所用的时间。在直......
  • B2006 地球人口承载力估计--解题思路
    地球人口承载力估计地球人口承载力估计题目描述假设地球上的新生资源按恒定速度增长。照此测算,地球上现有资源加上新生资源可供\(x\)亿人生活\(a\)年,或供\(y\)亿......
  • [Cnoi2020]光图-解题思路
    P6159-Cnoi2020光图\(通过观察样例,我们会发现光线先是从0号点射向p号点,然后再射到p\times2号点,然后是p\times3号点,以此类推。那么射到的第k个点应该就是编号为p\tim......
  • 浅析排列组合、斯特林数、贝尔数、二项式定理与推论及其反演、子集反演、广义容斥
    浅析排列组合、斯特林数、贝尔数、二项式定理与推论及其反演、子集反演、广义容斥目录浅析排列组合、斯特林数、贝尔数、二项式定理与推论及其反演、子集反演、广义容斥更......
  • 拓展思维的一题多解题
    前言在新人教A版看到这个经典的题目[1],联想到以前的相应解法,现对各种思路作以总结,体会下:我们积累的数学知识越多,解题的思路就越开阔,越顺畅。典例剖析如图所示,已知平......
  • 「解题报告」[NOI2022] 冒泡排序
    前排膜拜happyguy感觉这种特殊性质给的很多的题就应该把特殊性质挨个进行分析。特殊性质A首先容易发现:\(V_i\in[0,1]\),那么\(a_i\in[0,1]\)。显然这样不劣。......