「解题报告」AHOI2022 排列

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


首先 f(i,j)0 当且仅当 ai,aj 在同一个置换环中 .

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

对于环长 {ck} 满足关系 i=1kcin,于是本质不同的 ci 只有 O(n) 种,可以暴力枚举两种 ci .

首先处理初始答案,然后每次合并 ci,cjci,cj,ci+cj 分解质因数然后重新计算贡献即可 .

时间复杂度 Θ(nlogn) .

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 的 .

posted @   yspm  阅读(88)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
历史上的今天:
2022-02-18 丽泽普及2022交流赛day21 社论
😅​
点击右上角即可分享
微信分享提示