【2022 CCPC Henan Provincial Collegiate Programming Contest #K】复合函数

题目链接

K.复合函数

输入文件: standard input
输出文件: standard output
时间限制: 1 second
空间限制: 512 megabytes
给定正整数\(n\),并记\(I_n={1,2,\cdots,n}\)
给定一个定义在\(I_n\)上的函数\(f\),满足对于任意\(x\in I_n\)\(f(x)\in I_n\)
对于任意正整数\(k\),当\(k \gt 1\)时定义\(f^k(x)=f(f^{k−1}(x))\),特殊地,当\(k = 1\)时定义\(f^1(x)=f(x)\)
现有\(q\)组询问,每组询问给定两个正整数\(a,b\),试求出有多少个\(x \in I_n\)满足\(f^a(x)=f^b(x)\)

输入格式

第一行,一个正整数\(n(1 \leq n \leq 10^5)\),表示\(f\)的定义域的大小。
第二行,\(n\)个正整数\(f(1),f(2),\cdots,f(n)(1 \leq f(x) \leq n)\),表示函数\(f\)的取值。
第三行,一个正整数\(q(1 \leq q \leq 10^5)\),表示询问组数。
接下来\(q\)行,每行两个正整数\(a,b(1 \leq a,b \leq 10^{18})\),表示一组询问。

输出格式

输出共\(q\)行。对于每组询问,输出一行,一个整数,表示满足\(f^a(x)=f^b(x)\)\(x\)的个数。

样例

standart input standart output
1
1
1
1 1
1
6
1 1 4 5 1 4
5
1 919810
19 19810
191 9810
1919 810
19198 10
3
6
6
6
6

题解

因为值域和定义域都为\([1,n]\),所以我们可以把函数看成每个\(x\)节点连一条边到\(f(x)\)的图。
那么我们得到的图就是数个环在加上一些指向环的“链”。
那么对于每个环上的节点,只要\(|a-b|\)是环的大小的倍数,这个点就能被计入答案,而对于那些不在环上的点,当它们经过一定步数后进入环,然后被计入答案的条件就和环上的节点一样了,但是需要它先进入环,也就是说这种节点多了个限制条件,也就是\(a\)(我们不妨令\(a \lt b\))必须大于等于这个几点进入环需要的步数。
下面我们先考虑只有环的情况,我们可以用\(ans[i]\)表示在环的大小为\(i\)的环上的节点有多少个,我们可以推出不同大小的环的数量最多为\(\sqrt{n}\),那么我们就能做到\(O(q\sqrt{n})\)了。
接下来考虑不全是环的情况,那么就会有点存在限制,我们可以用\(ans[i][j]\)表示最终走到的环的大小为\(i\),限制为\(j\)的节点的个数。
但这样直接枚举的话我们的时间复杂度就又退化到\(O(qn)\)了。
因为只要限制小于等于\(a\)的节点都有可能被计入答案,所以我们维护一下前缀和,\(ans[i][j]\)表示最终走到的环的大小为\(i\),限制小于等于\(j\)的节点的个数。
注意:\(a=b\)的时候,不需要满足限制就能被计入答案,需要特判。
上代码:

#include<bits/stdc++.h>
using namespace std;
int n,q;
int to[100009];
int s[100009],di[100009];
vector<int>ans[100009];
int lans[100009];
bool k[100009];
long long a,b;
int qwe;
void add(int u,int v){
	if(to[u]!=qwe){
		add(to[u],v+1);
		s[u]=s[to[u]];
	}else{
		s[u]=v;
		if(lans[v]==0){
			ans[v].push_back(v);
			lans[v]++;
		}
		else ans[v][0]+=v;
	}
}
void dfs(int u){
	k[u]=1;
	if(k[to[u]]){
		qwe=to[u];
		add(to[u],1);
	}
	else if(s[to[u]]==0){
		dfs(to[u]);
	}
	if(s[u]==0){
		di[u]=di[to[u]]+1;
		s[u]=s[to[u]];
		if(lans[s[u]]<=di[u]){
			ans[s[u]].push_back(1);
			lans[s[u]]++;
		}
		else ans[s[u]][di[u]]++;
	}
	k[u]=0;
}
long long pp[100009];
int lp;
int main() {
	scanf("%d",&n);
	for(int j=1;j<=n;j++)
		scanf("%d",&to[j]);
	for(int j=1;j<=n;j++)
		if(s[j]==0) dfs(j);
	scanf("%d",&q);
	for(int j=1;j<=n;j++)
		if(lans[j]!=0){
			pp[++lp]=j;
			for(int i=1;i<lans[j];i++){
				ans[j][i]+=ans[j][i-1];
			}
		}
	while(q--){
		scanf("%lld%lld",&a,&b);
		if(a>b) swap(a,b);
		int sum=0;
		for(int j=1;j<=lp;j++){
			if(a==b) sum+=ans[pp[j]][lans[pp[j]]-1];
			else if((b-a)%pp[j]==0){
				if(a>=lans[pp[j]]-1) sum+=ans[pp[j]][lans[pp[j]]-1];
				else sum+=ans[pp[j]][a];
			}
		}
		printf("%d\n",sum);
	}
	return 0;
}
posted @ 2022-10-16 21:26  oblivionl  阅读(76)  评论(0编辑  收藏  举报