哇做不来数论题…
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曾不想做的数论题
不会啊…不会啊…先挖个坑
可能不会填了吧
感觉整场都在乱搞卡常…