[暴力搜索 剪枝 约数和反函数] BZOJ 3629 [JLOI2014]聪明的燕姿

约数和公式:sumd=(1+p1+p1^2+...+p1^a1)*...*(1+p1+p1^2+...+pn^an)

我们枚举出所有形如1+pi+pi^2+...+pi^ai 放入hashmap

然后对于约数和S分解因数 这里我们采用分解质因数后dfs的方法 时间复杂度是O(约数个数) 不会太大 最多一百多

然后对于每个因数 去hashmap里查找对应的质数幂和 有些因数无对应 有些可能有多个对应 总之还是不会太多

然后暴搜一通 加上个整除剪枝 结果果然跑的飞快


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}

inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc());
}

namespace Miller{
  inline int random(int n){
    return (int)((double)rand()/(RAND_MAX+1)*n);
  }
  inline int gcd(int a,int b){
    return b!=0?gcd(b,a%b):a;
  }
  inline int Mul(int a,int b,int p){
    a%=p; b%=p;
    int t=(a*b-(int)((double)a/p*b+0.5)*p);
    return t<0?t+p:t;
  }
  inline int Pow(int a,int b,int p){
    int t=1;
    for(;b;b>>=1,a=Mul(a,a,p))
      if(b&1)
	t=Mul(t,a,p);
    return t;
  }
  inline bool Witness(int a,int p,int s,int t){
    int x0,x1;
    x0=Pow(a,t,p);
    while (s--){
      x1=Mul(x0,x0,p);
      if (x1==1 && x0!=1 && x0!=p-1)
	return true;
      x0=x1;
    }
    if (x0!=1) return true;
    return false;
  }
  inline bool Miller(int p){
  	if (p==9)
  	int c=1;
    if (p<2) return false;
    if (p==2 || p==3) return true;
    if (~p&1) return false;
    if(p%6!=1 && p%6!=5) return false; 
    int s=0,t=p-1;
    while (~t&1) s++,t>>=1;
    int a;
    for (int i=1;i<=5;i++){
      a=random(p-1)+1;
      if (Witness(a,p,s,t))
	return false;
    }
    return true;
  }
}

const int MAXN=100005;

int prime[MAXN],vst[MAXN],num;

inline void Init(int maxn){
  for (int i=2;i<=maxn;i++){
    if (!vst[i]) prime[++num]=i;
    for (int j=1;j<=num && (ll)prime[j]*i<=maxn;j++){
      vst[i*prime[j]]=1;
      if (i%prime[j]==0) break;
    }
  }
}

int S,Sqr;

int d[1005],cnt;  
int pri[25],q[25],len;

void dfsd(int t,int cur){  
  if(t==len+1) { d[++cnt]=cur; return; }    
  dfsd(t+1,cur);  
  for (int i=1;i<=q[t];i++)  
    dfsd(t+1,cur*=pri[t]);  
}  

inline void Mac(){  
  int tem=S; len=0;  
  for (int i=1;i<=num && (ll)prime[i]*prime[i]<=tem;i++)  
    if (tem%prime[i]==0){  
      pri[++len]=prime[i]; q[len]=0;  
      while (tem%prime[i]==0) tem/=prime[i],q[len]++;  
    }  
  if (tem!=1) pri[++len]=tem,q[len]=1;  
  cnt=0;  
  dfsd(1,1);  
}

const int M=2000005;
const int P=1000007;

struct data{
  int x,p,a,pa,next;
}G[M];
int head[P],inum;

inline void add(int x,int p,int a,int pa){
  int t=x%P; G[++inum].x=x; G[inum].p=p; G[inum].a=a; G[inum].pa=pa; G[inum].next=head[t]; head[t]=inum;
}

struct abcd{
  int x,p,a,pa;
  abcd(int x=0,int p=0,int a=0,int pa=0):x(x),p(p),a(a),pa(pa) { if (p==9)
  int c=1;
   }
  bool operator < (const abcd &B) const{
    return pa<B.pa;
  }
}lst[100005];
int tot;

inline void Pd(int x){
  if (Miller::Miller(x-1)) 
    lst[++tot]=abcd(x,x-1,1,x-1);
  for (int p=head[x%P];p;p=G[p].next)
    if (G[p].x==x)
      lst[++tot]=abcd(x,G[p].p,G[p].a,G[p].pa);
}

int del[100005];

int Ans[100005],Tot;
int ilst[100005],ipnt;

inline void dfs(int t,int cur,int ret){
  if (cur==S){
    Ans[++Tot]=ret;
    return;
  }
  if (t==tot+1) return;
  if ((ll)cur*lst[t].x<=S && S%(cur*lst[t].x)==0 && (lst[t].p>Sqr || !del[lst[t].p])){
    if (lst[t].p<=Sqr) del[lst[t].p]=1;
	ilst[++ipnt]=t;
    dfs(t+1,cur*lst[t].x,ret*lst[t].pa);
    ilst[ipnt--]=0;
    if (lst[t].p<=Sqr) del[lst[t].p]=0;
  }
  dfs(t+1,cur,ret);
}


int main(){
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(S);
  Init(Sqr=sqrt(S));
  for (int i=1;i<=num;i++){
    ll sum=1,tem=1;
    for (int j=1;;j++){
      tem*=prime[i];
      sum+=tem;
      if (sum>S) break;
      add(sum,prime[i],j,tem);
    }
  }
  Mac();
  sort(d+1,d+cnt+1);
  for (int i=1;i<=cnt;i++)
    Pd(d[i]);
  sort(lst+1,lst+tot+1);
  dfs(1,1,1);
  sort(Ans+1,Ans+Tot+1);
  Tot=unique(Ans+1,Ans+Tot+1)-Ans-1;
  printf("%d\n",Tot);
  for (int i=1;i<=Tot;i++)
    printf("%d ",Ans[i]);
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值