[Contest] 算法马拉松. 32

哇做不来数论题…

A. 特殊表示法

这题搞了好久…把两个特殊表示法加起来,只要能处理2就好了
有两种情况
...21......1.1..
...2....1..1..

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <assert.h>

using namespace std;

const int N=5000010;

int n,m,l,L,a[N],b[N],c[N];

inline void Simplify(int *a,int &n){
  for(int i=1,j;i<=n;i=j+1){
    j=i;
    if(a[i]!=1) continue;
    while(a[j+1]==1 && j<n) j++;
    for(int k=j;k>i;k-=2){
      a[k]--,a[k-1]--,a[k+1]++;
      if(k==n) n++;
    }
  }
}

inline void sect(int x){
  L=max(L,x);
  if(c[x]<2 || x<1) return ;
  if(x==1){
    Simplify(c+1,l);
    if(c[2]) c[x]--,c[2]=0,c[3]=1;
    else c[x]-=2,c[2]=1;
    return ;
  }
  c[x]-=2;
  if(x>2){
    c[x-2]++; c[x+1]++; sect(x-2); sect(x+1); sect(x);
  }
  else{
    c[1]+=4; sect(1); sect(x);
  }
}

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

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

int main(){
  read(n);
  for(int i=1;i<=n;i++) read(a[i]); while(!a[n] && n>1) n--;
  read(m);
  for(int i=1;i<=m;i++) read(b[i]); while(!b[m] && m>1) m--;
  Simplify(a,n); Simplify(b,m);
  l=max(n,m); L=l;
  for(int i=1;i<=l;i++) c[i]=a[i]+b[i];
  Simplify(c,L);
  for(int i=L;i>2;i--){
    if(c[i]>1){
      if(c[i+1]){
    c[i+2]++; c[i+1]--; c[i]--;
    if(c[i]<2) continue;
      }
      c[i]-=2; c[i-2]++; c[i+1]++;
      if(i==L) L++;
    }
  }
  if(c[2]>1){
    c[2]-=2; c[1]+=4;
  }
  Simplify(c,L);
  assert(c[1]<=4);
  while(c[1]>1){
    if(c[2]) c[3]++,c[2]--,c[1]--;
    else c[2]++,c[1]-=2;
    Simplify(c,L);
  }
  printf("%d\n",L);
  for(int i=1;i<=L;i++) assert(c[i]<2),putchar(c[i]+'0'),putchar(' ');
  return 0;
}
B. 小树

暴力打出表,扔给OEIS…
http://oeis.org/A055314

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

const int P=1e9+7,N=1000010;

int s[1010][1010];
int fac[N],inv[N];

inline int C(int x,int y){
  return 1LL*fac[x]*inv[y]%P*inv[x-y]%P;
}

int f(int n,int m){
  if(n==2 && m==2) return 1;
  if(n==1 && m==1) return 1;
  if(n<=m) return 0;
  if(n==1) return 1;
  if(m==1) return 0;
  int ret=0;
  for(int i=1;i<=m;i++)
    ret=(ret+1LL*fac[i]*s[m][i]%P*f(n-m,i))%P;
  return 1LL*ret*C(n,m)%P;
}

inline void Pre(){
  fac[0]=1; for(int i=1;i<=1000000;i++) fac[i]=1LL*fac[i-1]*i%P;
  inv[1]=1; for(int i=2;i<=1000000;i++) inv[i]=1LL*(P-P/i)*inv[P%i]%P;
  inv[0]=1; for(int i=1;i<=1000000;i++) inv[i]=1LL*inv[i-1]*inv[i]%P;
}

inline int Pow(int x,int y){
  int ret=1; 
  for(;y;y>>=1,x=1LL*x*x%P) if(y&1) ret=1LL*ret*x%P;
  return ret;
}

int main(){
  s[0][0]=1; Pre();
  int ans=0,n,k; cin>>n>>k;
  if(n<k) return puts("0"),0;
  if(n==1 && k==1) return puts("0"),0;
  if(n==2 && k==2) return puts("1"),0;
  if(k==1) return puts("0"),0; n--;
  for(int i=1;i<=n+1-k;i++)
    if((n-k-i+1)&1) ans=(ans-1LL*C(n+1-k,i)*Pow(i,n-1))%P;
    else ans=(ans+1LL*C(n+1-k,i)*Pow(i,n-1))%P;
  ans=1LL*ans*C(n+1,k)%P;
  printf("%d\n",(ans+P)%P);
  return 0;
}
C. 如何愉快地与STL玩耍

能去重的数据结构还有什么?BITSET啊,线段树每个节点开一个BITSET搞一搞就行了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

typedef unsigned int U;

const int base=32,N=65536+3,M=10000;

int size[1<<16|5];

inline int count(U x){
  return size[x&65535]+size[x>>16];
}

struct BIT{
  U a[M/base+3];
  BIT(){ for(int i=0;i<=M/base;i++) a[i]=0; }
  void set(int x){ a[x/base]|=(1<<(x&31)); }
  friend BIT operator |(BIT x,BIT y){
    for(int i=0;i<=M/base;i++) x.a[i]|=y.a[i];
    return x;
  }
  BIT operator |=(BIT x){ return *this=*this|x; }
  int K_th(int k){
    for(int i=0;i<=M/base;i++){
      if(count(a[i])>=k){
    for(int j=0;j<32;j++)
      if((a[i]>>j&1) && !--k) return (i<<5)|j;
      }
      else k-=count(a[i]);
    }
    return -1;
  }
}a[N*3],b[N*3];

int n,q,S;

void Modify(int g,int l,int r,int L,int R,int k){
  b[g].set(k);
  if(L==l && r==R) return a[g].set(k);
  int mid=L+R>>1;
  if(r<=mid) Modify(g<<1,l,r,L,mid,k);
  else if(l>mid) Modify(g<<1|1,l,r,mid+1,R,k);
  else Modify(g<<1,l,mid,L,mid,k),Modify(g<<1|1,mid+1,r,mid+1,R,k);
}

BIT Query(int g,int l,int r,int L,int R){
  if(l==L && r==R) return a[g]|b[g];
  int mid=L+R>>1;
  if(r<=mid) return Query(g<<1,l,r,L,mid)|a[g];
  else if(l>mid) return Query(g<<1|1,l,r,mid+1,R)|a[g];
  else return Query(g<<1,l,mid,L,mid)|Query(g<<1|1,mid+1,r,mid+1,R)|a[g];
}

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

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

void PutAns(int x){
  if(x==-1) return putchar('-'),putchar('1'),void();
  if(x>=10) PutAns(x/10); putchar(x%10+'0');
}

int main(){
  for(int i=1;i<(1<<16);i++) size[i]=size[i>>1]+(i&1);
  read(n); read(q);
  for(int i=1;i<=q;i++){
    int opt,l,r,k;
    read(opt); read(l); read(r); read(k);
    if(opt==1) Modify(1,l,r,1,n,k);
    else PutAns(Query(1,l,r,1,n).K_th(k)),putchar('\n');
  }
  return 0;
}
D. Clarke and string

套路题…每个串有 O(|Si|) 个回文字串,用马拉车或者回文自动机求出每个串的回回文子串,用map记一下hash值,每次询问枚举个数少的串,到另一个串的map里找就好了,记忆化一下复杂度就科学了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <tr1/unordered_map>
#include <map>

using namespace std;
using namespace tr1;

typedef unsigned long long ll;
typedef pair<int,int> par;

const int N=100010,P=31,AB=26;

vector<ll> H[N];
unordered_map<ll,bool> M[N];
map<par,int> S;

int q,p,n,cnt,len[N],size[N],fail[N],nxt[N][AB+5],trans[N][AB+5];
char a[N];
ll hs[N],pw[N];

inline void Push(int x){
  int c=a[x]-'a';
  if(a[x-len[p]-1]-'a'!=c) p=trans[p][c];
  if(!nxt[p][c]){
    int cur=++cnt,k=fail[p]; len[cur]=len[p]+2;
    if(a[x-len[k]-1]-'a'==c) fail[cur]=nxt[k][c];
    else fail[cur]=nxt[trans[k][c]][c];
    memcpy(trans[cur],trans[fail[cur]],sizeof(trans[cur]));
    trans[cur][a[x-len[fail[cur]]]-'a']=fail[cur];
    nxt[p][c]=cur;
  }
  p=nxt[p][c];
}

inline ll GetSub(int l,int r){
  return hs[r]-hs[l-1]*pw[r-l+1];
}

int main(){
  scanf("%d",&n);
  pw[0]=1; for(int i=1;i<=100000;i++) pw[i]=pw[i-1]*P;
  for(int i=1;i<=n;i++){
    scanf("%s",a+1); size[i]=strlen(a+1);
    for(int j=0;j<=cnt;j++)
      for(int k=0;k<AB;k++)
    trans[j][k]=nxt[j][k]=0;
    fail[0]=fail[1]=1; len[1]=-1; cnt=1; p=0;
    for(int j=1;j<=size[i];j++)
      hs[j]=hs[j-1]*P+a[j]-'a'+1;
    for(int j=0;j<AB;j++) trans[0][j]=1;
    for(int j=1;j<=size[i];j++)
      Push(j),M[i][GetSub(j-len[p]+1,j)]=1;
  }
  /*for(auto u : M[2]) cout<<u.first<<endl;
  cout<<endl;
  for(auto u : M[6]) cout<<u.first<<endl;*/
  scanf("%d",&q); int lst=0;
  for(int i=1;i<=q;i++){
    int x,y; scanf("%d%d",&x,&y); x^=lst; y^=lst;
    if(M[x].size()>M[y].size() || (M[x].size()==M[y].size() && x>y)) swap(x,y);
    if(S.count(par(x,y))){
      printf("%d\n",lst=S[par(x,y)]); continue;
    }
    lst=0;
    for(auto u : M[x])
      if(M[y].count(u.first)) lst++;
    printf("%d\n",S[par(x,y)]=lst);
  }
  return 0;
}
E. 阶乘

把这些阶乘质因数分解,每次二分求当前的 cj! ,把 cj! 质因数分解, ej 就是对应质因数次数的商的最小值,答案应该是不超过 O(n) 的。
暴力做质因数分解的复杂度就是 O(n×π(n)) 的了。
假设我们要分解 x! ,小于等于 x 的质因数直接搞,大于 x 的质因数出现 xp 次,这东西取值有 O(x) 个,分块做差分维护

那二分判断的时候呢,差分改成线段树就好了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <ctime>
#include <assert.h>

using namespace std;

typedef long long ll;

const int N=100010;

int n,t,a[N],p[N],lc[N];
ll c[N],b[N],d[N];
ll ans0[N],ans1[N];

inline void Pre(){
  for(int i=2;i<=100000;i++){
    if(!p[i]){
      p[++*p]=i;
    }
    for(int j=1;j<=*p && 1LL*p[j]*i<=100000;j++){
      p[p[j]*i]=1;
      if(i%p[j]==0) break;
    }
  }
  lc[1]=1;
  for(int i=1;i<*p;i++)
    for(int j=p[i];j<p[i+1];j++) lc[j]=i+1;
  for(int i=p[*p];i<=100003;i++) lc[i]=(*p)+1;
}

inline void add(int x,int cc){
  int pos=*p+1;
  for(int i=1;p[i]<=x && i<=*p;i++){
    if(1LL*p[i]*p[i]>x){
      pos=i; break;
    }
    int y=x;
    while(y) c[i]+=1LL*(y/p[i])*cc,y/=p[i];
  }
  for(int i=pos,j;i<=*p && p[i]<=x;i=j){
    b[i]+=1LL*cc*(x/p[i]);
    if(x/(x/p[i])<=100000) j=lc[x/(x/p[i])];
    else j=(*p)+1;
    b[j]-=1LL*cc*(x/p[i]);
  }
}

ll mx[N<<2],mn[N<<2],tag[N<<2];

inline void addt(int g,ll x){
  mx[g]+=x; mn[g]+=x; tag[g]+=x;
}

inline void Push(int g){
  if(!tag[g]) return ;
  addt(g<<1,tag[g]); addt(g<<1|1,tag[g]);
  tag[g]=0;
}

inline void Up(int g){
  mx[g]=max(mx[g<<1],mx[g<<1|1]);
  mn[g]=min(mn[g<<1],mn[g<<1|1]);
}

void Build(int g,int l,int r){
  if(l==r) return mx[g]=mn[g]=c[l],void();
  int mid=l+r>>1;
  Build(g<<1,l,mid); Build(g<<1|1,mid+1,r);
  Up(g);
}

void Add(int g,int l,int r,int L,int R,ll d){
  if(l==L && r==R) return addt(g,d);
  int mid=L+R>>1; Push(g);
  if(r<=mid) Add(g<<1,l,r,L,mid,d);
  else if(l>mid) Add(g<<1|1,l,r,mid+1,R,d);
  else Add(g<<1,l,mid,L,mid,d),Add(g<<1|1,mid+1,r,mid+1,R,d);
  Up(g);
}

ll QueryMx(int g,int l,int r,int L,int R){
  if(l==L && r==R) return mx[g];
  int mid=L+R>>1; Push(g);
  if(r<=mid) return QueryMx(g<<1,l,r,L,mid);
  if(l>mid) return QueryMx(g<<1|1,l,r,mid+1,R);
  return max(QueryMx(g<<1,l,mid,L,mid),QueryMx(g<<1|1,mid+1,r,mid+1,R));
}

ll QueryMn(int g,int l,int r,int L,int R){
  if(l==L && r==R) return mn[g];
  int mid=L+R>>1; Push(g);
  if(r<=mid) return QueryMn(g<<1,l,r,L,mid);
  if(l>mid) return QueryMn(g<<1|1,l,r,mid+1,R);
  return min(QueryMn(g<<1,l,mid,L,mid),QueryMn(g<<1|1,mid+1,r,mid+1,R));
}

inline bool check(int x){
  int pos=*p+1;
  for(int i=1;p[i]<=x;i++){
    if(p[i]*p[i]>x){
      pos=i; break;
    }
    int y=x,cur=0;
    while(y) cur+=y/p[i],y/=p[i];
    if(cur>QueryMx(1,i,i,1,*p)) return false;
  }
  for(int i=pos,j;p[i]<=x;i=j){
    j=lc[x/(x/p[i])];
    if(QueryMn(1,i,j-1,1,*p)<x/p[i]) return false;
  }
  return true;
}

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

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

void PutAns(ll x){
  if(x>=10) PutAns(x/10); putchar(x%10+'0');
}

int main(){
  read(n); Pre();
  for(int i=1;i<=n;i++) read(a[i]);
  sort(a+1,a+1+n);
  for(int i=1,j;i<=n;i=j){
    for(j=i;a[j]==a[i] && j<=n;j++);
    add(a[i],j-i);
  }
  for(int i=1;i<=*p;i++) c[i]+=(b[i]+=b[i-1]);
  Build(1,1,*p);
  int lst=100002; p[(*p)+1]=100003;
  while(1){
    int L=1,R=lst,mid,Lst=R;
    while(L<=R){
      mid=L+R>>1;
      if(L<Lst-20 && R>Lst-20) mid=Lst-20;
      check(mid)?L=(lst=mid)+1:R=mid-1;
    }
    ll ci=1e18;
    if(lst==1) break;
    int pos=*p+1;
    for(int i=1;p[i]<=lst;i++){
      if(p[i]*p[i]>lst){
    pos=i; break;
      }
      int y=lst; b[i]=0;
      while(y) b[i]+=y/p[i],y/=p[i];
      ci=min(ci,QueryMn(1,i,i,1,*p)/b[i]);
    }
    for(int i=pos,j;p[i]<=lst;i=j){
      if(lst/(lst/p[i])<=100000) j=lc[lst/(lst/p[i])];
      else j=(*p)+1;
      ci=min(ci,QueryMn(1,i,j-1,1,*p)/(lst/p[i]));
    }
    for(int i=1;p[i]<=lst;i++){
      if(p[i]*p[i]>lst){
    pos=i; break;
      }
      Add(1,i,i,1,*p,-1LL*ci*b[i]);
    }
    for(int i=pos,j;p[i]<=lst;i=j){
      j=lc[lst/(lst/p[i])];
      Add(1,i,j-1,1,*p,-1LL*ci*(lst/p[i]));
    }
    ans0[++t]=lst; ans1[t]=ci;
  }
  printf("%d\n",t);
  for(int i=1;i<=t;i++)
    PutAns(ans0[i]),putchar(' '),PutAns(ans1[i]),putchar('\n');
  return 0;
}
F. Jason曾不想做的数论题

不会啊…不会啊…先挖个坑
可能不会填了吧


感觉整场都在乱搞卡常…

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值