• test20200411 选数


    选数

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

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

    (Tleq 500,pleq 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
    ",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;
    }
    
  • 相关阅读:
    用python比对csv文件中的数据
    markdown图床
    java里一些类所属的包
    edge浏览器中http网站url不显示http只显示不安全
    刮卡一样处理自己的图片
    rss之光——irreader
    lombok的Data注解没有使setter起作用
    某端口被占用的解决方法
    如何避免公司加班的神坑
    面试题8:旋转数组的最小数字
  • 原文地址:https://www.cnblogs.com/autoint/p/12685964.html
Copyright © 2020-2023  润新知