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;
}
静渊以有谋,疏通而知事。