题解 [AHOI2022] 排列

传送门

发现这个操作就是一次置换
考虑回到原序列所需最小次数就是所有置换环大小的 lcm
还有个交换两项很烦
但发现交换置换环 A 中一项和 B 中一项产生的影响相当于删掉 A, B 加入一个 \(siz_c=siz_a+siz_b\) 的 C
那么发现大小相同的置换环是等价的
又有 \(\sum siz_i=n\),所以不同的 siz 只有根号个
那么可以 \(n^2\) 枚举 siz,现在的问题是计算 lcm
发现可以对每个质因子开个 multiset 存在每个数中(若那个数中出现了这个质因子)出现的幂次
那么这样是 \(O(n\log^2 n)\)
发现有特殊性质是最多删两个数
所以不用 multiset,维护一下最大次大次次大之类的东西就可以 \(O(n\log n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int a[N];
const ll mod=1e9+7;

// namespace force{
// 	int p[2010][2010], p2[2010][2010];
// 	int qval() {
// 		// cout<<"qval: "; for (int i=1; i<=n; ++i) cout<<a[i]<<' '; cout<<endl;
// 		for (int i=1; i<=n; ++i) p2[0][i]=i;
// 		for (int i=1; i<=n*n+1; ++i) {
// 			for (int j=1; j<=n; ++j)
// 				p2[i][j]=p2[i-1][a[j]];
// 			// cout<<"p("<<i<<"): "; for (int j=1; j<=n; ++j) cout<<p2[i][j]<<' '; cout<<endl;
// 		}
// 		for (int k=1; ; ++k) {
// 			for (int i=1; i<=n; ++i) if (p2[k+1][i]!=a[i]) goto jump2;
// 			return k;
// 			jump2: ;
// 		}
// 	}
// 	void solve() {
// 		int ans=0;
// 		for (int i=1; i<=n; ++i) p[0][i]=i;
// 		for (int i=1; i<=n+1; ++i)
// 			for (int j=1; j<=n; ++j)
// 				p[i][j]=p[i-1][a[j]];
// 		for (int i=1; i<=n; ++i) {
// 			for (int j=1,t; j<=n; ++j) {
// 				for (int k=0; k<=n+1; ++k) if (p[k][i]==j) goto jump;
// 				swap(a[i], a[j]);
// 				t=qval(); ans+=t;
// 				// cout<<"f("<<i<<','<<j<<")="<<t<<endl;
// 				swap(a[i], a[j]);
// 				jump: ;
// 			}
// 		}
// 		printf("%d\n", ans);
// 	}
// }

// namespace task1{
// 	int p[2010][2010], dsu[N], siz[N];
// 	inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
// 	inline int gcd(int a, int b) {return !b?a:gcd(b, a%b);}
// 	inline int lcm(int a, int b) {return a/gcd(a, b)*b;}
// 	inline void uni(int a, int b) {if ((a=find(a))!=(b=find(b))) siz[dsu[a]=b]+=siz[a];}
// 	int qval() {
// 		for (int i=1; i<=n; ++i) siz[dsu[i]=i]=1;
// 		for (int i=1; i<=n; ++i) uni(i, a[i]);
// 		int ans=1;
// 		for (int i=1; i<=n; ++i) if (i==find(i)) ans=lcm(ans, siz[i]);
// 		return ans;
// 	}
// 	void solve() {
// 		int ans=0;
// 		for (int i=1; i<=n; ++i) p[0][i]=i;
// 		for (int i=1; i<=n*n+1; ++i)
// 			for (int j=1; j<=n; ++j)
// 				p[i][j]=p[i-1][a[j]];
// 		for (int i=1; i<=n; ++i) {
// 			for (int j=1,t; j<=n; ++j) {
// 				for (int k=0; k<=n+1; ++k) if (p[k][i]==j) goto jump;
// 				swap(a[i], a[j]);
// 				t=qval(); ans+=t;
// 				// cout<<"f("<<i<<','<<j<<")="<<t<<endl;
// 				swap(a[i], a[j]);
// 				jump: ;
// 			}
// 		}
// 		printf("%d\n", ans);
// 	}
// }

// namespace task2{
// 	int bel[N], dsu[N], siz[N];
// 	inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
// 	inline int find2(int p) {return bel[p]==p?p:bel[p]=find2(bel[p]);}
// 	inline void uni(int a, int b) {if ((a=find(a))!=(b=find(b))) siz[dsu[a]=b]+=siz[a];}
// 	inline ll gcd(ll a, ll b) {return !b?a:gcd(b, a%b);}
// 	inline ll lcm(ll a, ll b) {return a/gcd(a, b)*b;}
// 	ll qval() {
// 		for (int i=1; i<=n; ++i) siz[dsu[i]=i]=1;
// 		for (int i=1; i<=n; ++i) uni(i, a[i]);
// 		ll ans=1;
// 		for (int i=1; i<=n; ++i) if (i==find(i)) ans=lcm(ans, siz[i]);
// 		return ans;
// 	}
// 	void solve() {
// 		ll ans=0;
// 		for (int i=1; i<=n; ++i) bel[i]=i;
// 		for (int i=1; i<=n; ++i) bel[find2(i)]=bel[find2(a[i])];
// 		for (int i=1; i<=n; ++i) {
// 			for (int j=1,t; j<=n; ++j) if (find2(i)!=find2(j)) {
// 				swap(a[i], a[j]);
// 				ans=(ans+qval())%mod;
// 				swap(a[i], a[j]);
// 			}
// 		}
// 		printf("%lld\n", ans);
// 	}
// }

// namespace task3{
// 	map<int, int> mp;
// 	ll pre[N], suf[N], ans;
// 	int dsu[N], siz[N], sta[N], cnt[N], top;
// 	inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
// 	inline void uni(int a, int b) {if ((a=find(a))!=(b=find(b))) siz[dsu[a]=b]+=siz[a];}
// 	inline ll gcd(ll a, ll b) {return !b?a:gcd(b, a%b);}
// 	inline ll lcm(ll a, ll b) {return a/gcd(a, b)*b;}
// 	void solve() {
// 		ans=top=0; mp.clear();
// 		for (int i=1; i<=n; ++i) siz[dsu[i]=i]=1;
// 		for (int i=1; i<=n; ++i) uni(i, a[i]);
// 		for (int i=1; i<=n; ++i) if (i==find(i)) ++mp[siz[i]];
// 		for (auto it:mp) sta[++top]=it.fir, cnt[top]=it.sec;
// 		pre[0]=suf[top+1]=1;
// 		for (int i=1; i<=top; ++i) pre[i]=lcm(pre[i-1], sta[i]);
// 		for (int i=top; i; --i) suf[i]=lcm(sta[i], suf[i+1]);
// 		// cout<<"sta: "; for (int i=1; i<=top; ++i) cout<<sta[i]<<' '; cout<<endl;
// 		// cout<<"cnt: "; for (int i=1; i<=top; ++i) cout<<cnt[i]<<' '; cout<<endl;
// 		for (int i=1; i<=top; ++i) {
// 			ll mid=pre[i-1];
// 			if (cnt[i]>1) mid=lcm(mid, sta[i]);
// 			for (int j=i+1; j<=top; ++j) {
// 				ll val=lcm(mid, suf[j+1]);
// 				if (cnt[j]>1) val=lcm(val, sta[j]);
// 				val=lcm(val, sta[i]+sta[j]);
// 				val%=mod;
// 				ans=(ans+2ll*sta[i]*cnt[i]%mod*sta[j]%mod*cnt[j]%mod*val)%mod;
// 				mid=lcm(mid, sta[j]);
// 			}
// 		}
// 		for (int i=1; i<=top; ++i) if (cnt[i]>=2) {
// 			ll val=lcm(pre[i-1], suf[i+1]);
// 			if (cnt[i]>2) val=lcm(val, sta[i]);
// 			val=lcm(val, sta[i]*2);
// 			val%=mod;
// 			ans=(ans+sta[i]*cnt[i]%mod*sta[i]%mod*(cnt[i]-1)%mod*val)%mod;
// 		}
// 		printf("%lld\n", ans);
// 	}
// }

namespace task4{
	bool npri[N];
	map<int, int> mp;
	multiset<int> tem[N];
	vector<ll> pw[N], ipw[N];
	ll pre[N], suf[N], ans, lcm;
	int pri[N], low[N], lowp[N], lowc[N], pcnt;
	int dsu[N], siz[N], sta[N], inv[N], cnt[N], top;
	inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
	inline void uni(int a, int b) {if ((a=find(a))!=(b=find(b))) siz[dsu[a]=b]+=siz[a];}
	inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}
	void init() {
		for (int i=2; i<N; ++i) {
			if (!npri[i]) pri[++pcnt]=low[i]=lowp[i]=i, lowc[i]=1;
			for (int j=1,x; j<=pcnt&&i*pri[j]<N; ++j) {
				npri[x=i*pri[j]]=1;
				lowp[x]=pri[j];
				if (!(i%pri[j])) {
					low[x]=low[i]*pri[j];
					lowc[x]=lowc[i]+1;
					break;
				}
				else low[x]=pri[j], lowc[x]=1;
			}
		}
		for (int i=1; i<=pcnt; ++i) inv[pri[i]]=qpow(pri[i], mod-2);
		for (int i=1; i<=pcnt; ++i) {
			pw[pri[i]].pb(1); ipw[pri[i]].pb(1);
			while (pri[i]*pw[pri[i]].back()<N) {
				pw[pri[i]].pb(pri[i]*pw[pri[i]].back());
				ipw[pri[i]].pb(inv[pri[i]]*ipw[pri[i]].back()%mod);
			}
		}
	}
	void add(int x) {
		for (int t=x; t>1; t/=low[t]) {
			int org=*tem[lowp[t]].rbegin();
			tem[lowp[t]].insert(lowc[t]);
			int now=*tem[lowp[t]].rbegin();
			lcm=lcm*pw[lowp[t]][now-org]%mod;
		}
	}
	void del(int x) {
		for (int t=x; t>1; t/=low[t]) {
			int org=*tem[lowp[t]].rbegin();
			tem[lowp[t]].erase(tem[lowp[t]].find(lowc[t]));
			int now=*tem[lowp[t]].rbegin();
			lcm=lcm*ipw[lowp[t]][org-now]%mod;
		}
	}
	void solve() {
		ans=top=0; mp.clear(); lcm=1;
		for (int i=1; i<=n; ++i) tem[i].clear(), tem[i].insert(0);
		for (int i=1; i<=n; ++i) siz[dsu[i]=i]=1;
		for (int i=1; i<=n; ++i) uni(i, a[i]);
		for (int i=1; i<=n; ++i) if (i==find(i)) ++mp[siz[i]];
		for (auto it:mp) sta[++top]=it.fir, cnt[top]=it.sec;
		for (int i=1; i<=top; ++i) add(sta[i]);
		for (int i=1; i<=top; ++i) {
			if (cnt[i]==1) del(sta[i]);
			for (int j=i+1; j<=top; ++j) {
				if (cnt[j]==1) del(sta[j]);
				add(sta[i]+sta[j]);
				ans=(ans+2ll*sta[i]*cnt[i]%mod*sta[j]%mod*cnt[j]%mod*lcm)%mod;
				del(sta[i]+sta[j]);
				if (cnt[j]==1) add(sta[j]);
			}
			if (cnt[i]==1) add(sta[i]);
		}
		for (int i=1; i<=top; ++i) if (cnt[i]>=2) {
			if (cnt[i]==2) del(sta[i]);
			add(sta[i]*2);
			ans=(ans+sta[i]*cnt[i]%mod*sta[i]%mod*(cnt[i]-1)%mod*lcm)%mod;
			del(sta[i]*2);
			if (cnt[i]==2) add(sta[i]);
		}
		printf("%lld\n", ans);
	}
}

signed main()
{
	freopen("perm.in", "r", stdin);
	freopen("perm.out", "w", stdout);

	int T=read();
	task4::init();
	while (T--) {
		n=read();
		for (int i=1; i<=n; ++i) a[i]=read();
		// force::solve();
		// task1::solve();
		// task2::solve();
		// task3::solve();
		task4::solve();
	}

	return 0;
}
posted @ 2022-06-08 07:09  Administrator-09  阅读(2)  评论(0编辑  收藏  举报