test20200411 选数

选数

给定质数\(p\),正整数\(a,b< p\),整数\(m\geq 0\)。你需要从\(0,1,\dots,p-1\)\(p\)个数中选出最多的数,满足对于任意非负整数\(x<m\),若\(ax^b\bmod p\)被选,则\((bx+a)\bmod p\)不能选。

求最多能选出的数的个数和选出最多的数的方案数除以\(10007\)的余数。

\(T\leq 500,p\leq 10^4\)

题解

一眼看过去就感觉是基环树……仔细分析一下,发现因为\(p\)是质数,所以\(\gcd(b,p)=1\)\(bx+a\)两两不同。如果把\((ax^b,bx+a)\)看成一条有向边的话,那么每个点的入边唯一。所以这是求基环外向树森林的最大独立集个数及方案数。

然后就是码农部分了。注意这题的坑点是有自环,如果没有想到这一点的话就会很惨。

时间复杂度 \(O(Tp)\)

IN int pow(int a,int b,int p){
	int ans=1;
	for(;b;b>>=1,a=a*a%p)
		if(b&1) ans=ans*a%p;
	return ans;
}

CO int N=1e4+10,mod=1e4+7;
vector<int> to[N];
int pos[N],low[N],dfn;
int stk[N],top,ins[N];
int col[N],idx,deg[N];
vector<int> scc[N];

void tarjan(int u){
	pos[u]=low[u]=++dfn;
	stk[++top]=u,ins[u]=1;
	for(int v:to[u]){
		if(!pos[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(ins[v]) low[u]=min(low[u],pos[v]);
	}
	if(low[u]==pos[u]){
		scc[++idx].clear();
		do{
			int x=stk[top];
			col[x]=idx,scc[idx].push_back(x),ins[x]=0;
		}while(stk[top--]!=u);
	}
}

bool flag[N];
pair<int,int> F[N][2];

IN pair<int,int> operator+(CO pair<int,int>&a,CO pair<int,int>&b){
	pair<int,int> ans={max(a.first,b.first),0};
	if(a.first>b.first)	ans.second=a.second;
	else if(a.first<b.first) ans.second=b.second;
	else ans.second=(a.second+b.second)%mod;
	return ans;
}
IN pair<int,int> operator*(CO pair<int,int>&a,CO pair<int,int>&b){
	return {a.first+b.first,a.second*b.second%mod};
}

void dfs(int u,int fa){
	F[u][0]={0,1};
	F[u][1]=flag[u]?(pair<int,int>){0,0}:(pair<int,int>){1,1};
	for(int v:to[u])if(v!=fa){
		dfs(v,u);
		F[u][0]=F[u][0]*(F[v][0]+F[v][1]);
		F[u][1]=F[u][1]*F[v][0];
	}
}
void real_main(int a,int b,int p,int m){
	for(int u=0;u<p;++u) to[u].clear();
	fill(flag,flag+p,0);
	for(int x=0;x<m;++x){
		int u=a*pow(x,b,p)%p,v=(b*x+a)%p;
		if(u==v) {flag[u]=1; continue;}
		to[u].push_back(v);
//		cerr<<"link "<<u<<" "<<v<<endl;
	}
	
	fill(pos,pos+p,0),dfn=0,idx=0;
	for(int u=0;u<p;++u)if(!pos[u]) tarjan(u);
	
//	for(int i=1;i<=idx;++i){
//		cerr<<i<<" scc=";
//		for(int u:scc[i]) cerr<<" "<<u;
//		cerr<<endl;
//	}
	
	fill(deg+1,deg+idx+1,0);
	for(int u=0;u<p;++u)for(int v:to[u]) deg[col[v]]+=col[u]!=col[v];
	
	pair<int,int> ans={0,1};
	for(int i=1;i<=idx;++i)if(!deg[i]){
		if(scc[i].size()==1){
			dfs(scc[i][0],-1);
//			pair<int,int> res=F[scc[i][0]][0]+F[scc[i][0]][1];
//			cerr<<i<<" res="<<res.first<<" "<<res.second<<endl;
			ans=ans*(F[scc[i][0]][0]+F[scc[i][0]][1]);
			continue;
		}
		
		for(int j=0;j<(int)scc[i].size();++j)
			dfs(scc[i][j],scc[i][(j+scc[i].size()-1)%scc[i].size()]);
		
		vector<array<pair<int,int>,2> > G(scc[i].size());
		G[0][0]=F[scc[i][0]][0],G[0][1]={0,0};
		for(int j=1;j<(int)scc[i].size();++j){
			G[j][0]=F[scc[i][j]][0]*(G[j-1][0]+G[j-1][1]);
			G[j][1]=F[scc[i][j]][1]*G[j-1][0];
		}
		pair<int,int> res=G[scc[i].size()-1][0]+G[scc[i].size()-1][1];
		
		G[0][0]={0,0},G[0][1]=F[scc[i][0]][1];
		for(int j=1;j<(int)scc[i].size();++j){
			G[j][0]=F[scc[i][j]][0]*(G[j-1][0]+G[j-1][1]);
			G[j][1]=F[scc[i][j]][1]*G[j-1][0];
		}
		res=res+G[scc[i].size()-1][0];
//		cerr<<i<<" res="<<res.first<<" "<<res.second<<endl;
		ans=ans*res;
	}
	printf("%d %d\n",ans.first,ans.second);
}
int main(){
	int T=read<int>(),a=read<int>(),b=read<int>();
	while(T--){
		int p=read<int>(),m=min(read<int>(),p);
		real_main(a,b,p,m);
	}
	return 0;
}

posted on 2020-04-12 16:19  autoint  阅读(121)  评论(0编辑  收藏  举报

导航