【考试总结】2022-01-13
守序划分问题
考虑这样一个结论:如果能将一个划分得到的集合分成两组且其中一组中所包含的所有数的最大值小于另一个集合中所有数的最小值,此时不满足划分是守序的,否则都是守序的
形式化的必要性证明考察的是在环排列里面每个点的后继之所属,设上面表述中最大值小的部分的集合为 \(S\),那么每个 \(S\) 中的集合在环上的后继都是属于 \(S\) 的,和不属于 \(S\) 的集合不能形成环排列
构造合法环排列的方式为:将所有划分得到的集合按照最小值排序,尝试逐个加入环排列
设这 \(m\) 个集合按照最小值从小到大排序之后得到的结果是 \(A_{q_1}\dots A_{q_m}\)
当前加入了集合 \(A_{q_1}\dots A_{q_k}\) ,并尝试加入 \(A_{q_{k+1}}\)。如果无法在这个线段中找到一个 \(A_{q_j}\) 满足其中最大值大于 \(A_{q_{k+1}}\) 最小值,由于后面集合最小值更大,那么就可以划分成两个部分了
至此考虑从 \(1\) 到 \(n\) 每个数字属于划分中的哪个集合,其中有些集合不再可以加入数字
设 \(dp_{i,j,k}\) 表示当前尝试加入 \(i\),已经在划分中添加了 \(j\) 个集合,其中有 \(k\) 个不能再加入数字,转移分新建一个集合与否即可,实现需要滚动数组
另外一个做法是容斥有几条分界线,转移系数是负斯特林数,甚至可以使用二维分治 FFT 优化至 \(\Theta(n^2\log^2n)\),当然如果你是像 Cirno_9/rsx 那样的 dalao 可以写一个多项式求逆,但是我不是
Code Display
const int N=510;
int n,m,dp[2][N][N];
signed main(){
freopen("partition.in","r",stdin); freopen("partition.out","w",stdout);
n=read(); m=read(); int cur=0;
dp[0][0][0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<=n;++j){
for(int k=0;k<=j;++k){
if(k==0&&i!=n) continue;
if(j>0){
dp[cur^1][j][k]=dp[cur][j-1][k];
if(k>0) ckadd(dp[cur^1][j][k],dp[cur][j-1][k-1]);
}
if(k<=j) ckadd(dp[cur^1][j][k],mul(dp[cur][j][k+1],k+1));
ckadd(dp[cur^1][j][k],mul(dp[cur][j][k],k));
}
}
for(int j=0;j<=n;++j){
for(int k=0;k<=j;++k) dp[cur][j][k]=0;
}
cur^=1;
}
print(dp[cur][m][0]);
return 0;
}
欧拉函数
有线段树维护 std::bitset
并使用 _Find_first,_Find_next
扫就行了
\(\rm poly \log\) 的做法是在对于每个数 \(i\) 计算其质因子集合,对于一个 \(prime|i\) 在二维平面中 \((i,i)\) 处加入权值为 \(w=1-\frac{1}p\) 的点,再找到这个质因子上一次出现的位置 \(k\) 和下一次出现的位置 \(j\),在点 \((k,i)\) 和 \((i,j)\) 加入一个权值为 \(\frac 1w\) 的点
根据这样子的构造,求解 \([l,r]\) 的信息就是对于 \(x\ge l,y\le r\) 的所有点权的乘积
变成了朴素的三位偏序就可以 cdq 分治了,剩下的区间和以及区间乘积使用 \(\rm Fenwick\ Tree\) 做就行了
如果强制在线可以使用 KD-Tree 来做到根号复杂度,不带 \(\log\)
Code Display
const int N=1e6+10,M=5e4+10;
int Q,a[M],n,pri[N],cnt;
bool fl[N];
struct BIT{
int c[N];
inline void push_add(int x,int v){
for(;x<=n;x+=x&(-x)) c[x]+=v;
return ;
}
inline void push_mul(int x,int v){
for(;x<=n;x+=x&(-x)) ckmul(c[x],v);
return ;
}
inline int query_sum(int x){
int res=0;
for(;x;x-=x&(-x)) res+=c[x];
return res;
}
inline int query_mul(int x){
int res=1;
for(;x;x-=x&(-x)) ckmul(res,c[x]);
return res;
}
}Tsum,Tmul;
int inv[40010];
bitset<4400>st[M<<2];
inline void push_up(int x){
st[x]=st[x<<1]|st[x<<1|1];
return ;
}
inline void build(int p,int l,int r){
if(l==r){
int x=a[l];
for(int i=1;x>1;++i) if(x%pri[i]==0){
st[p][i]=1;
while(x%pri[i]==0) x/=pri[i];
} return ;
}int mid=(l+r)>>1;
build(p<<1,l,mid); build(p<<1|1,mid+1,r);
return push_up(p);
}
bitset<4400>upd;
inline void Modify(int p,int l,int r,int pos){
if(l==r) return st[p]=upd,void();
int mid=(l+r)>>1;
if(pos<=mid) Modify(p<<1,l,mid,pos);
else Modify(p<<1|1,mid+1,r,pos);
return push_up(p);
}
inline void Query(int p,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return upd|=st[p],void(); int mid=(l+r)>>1;
if(ql<=mid) Query(p<<1,l,mid,ql,qr);
if(qr>mid) Query(p<<1|1,mid+1,r,ql,qr);
return ;
}
inline int Get_phi(int x){
int ans=x;
for(int i=1;i<=cnt&&pri[i]*pri[i]<=x;++i) if(x%pri[i]==0){
while(x%pri[i]==0) x/=pri[i];
ans=ans/pri[i]*(pri[i]-1);
}
if(x>1) ans=ans/x*(x-1);
return ans;
}
signed main(){
freopen("phi.in","r",stdin); freopen("phi.out","w",stdout);
n=1e6;
for(int i=2;i<=n;++i){
if(!fl[i]) pri[++cnt]=i;
for(int j=1;j<=cnt&&1ll*i*pri[j]<=n;++j){
fl[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
n=4e4; inv[1]=inv[0]=1;
for(int i=2;i<=n;++i) inv[i]=mod-mul(mod/i,inv[mod%i]);
n=read(); Q=read(); rep(i,1,n) a[i]=read(),Tsum.push_add(i,a[i]),Tmul.c[i]=1;
rep(i,1,n) Tmul.push_mul(i,a[i]);
build(1,1,n);
while(Q--){
int opt=read();
if(opt==0){
int i=read(),v=read();
upd.reset();
int x=v;
for(int i=1;x>1;++i) if(x%pri[i]==0){
upd[i]=1;
while(x%pri[i]==0) x/=pri[i];
}
Modify(1,1,n,i);
Tsum.push_add(i,v-a[i]);
Tmul.push_mul(i,mul(v,inv[a[i]]));
a[i]=v;
}else if(opt==1){
int l=read(),r=read();
print(Get_phi(Tsum.query_sum(r)-Tsum.query_sum(l-1)));
}else{
int l=read(),r=read();
int ans=mul(ksm(Tmul.query_mul(l-1),mod-2),Tmul.query_mul(r));
upd.reset();
Query(1,1,n,l,r);
for(int i=upd._Find_first();i!=4400;i=upd._Find_next(i)){
ckmul(ans,del(1,inv[pri[i]]));
} print(ans);
}
}
return 0;
}
点整的上圆
前置知识是 LOJ3069,直接改代码就能得到 60 分
而标算的计算方式是构造了一个 \(n\) 的质因子集合 \(D\) 向非负整数的映射,也可以理解成在 min25 筛的过程中维护 \(n\) 的每个因子的出现次数
这个映射过程的初态和直接求函数的质数处点值类似,找到 \(n\) 是否包含 \(2k+1\left(k\in[0,+\infty]\right)\) 这个因子,有则为 \(1\),无即是 \(0\)
函数的加法即对位加法,而乘法就是狄利克雷卷积,剩下的全都交给 min25 筛就做完了
这里实现的时候需要特别注意由于是维护映射,复杂度多带一个 \(d(n)\log d(n)\),卷积用平方实现会被卡
赛时的一些想法是使用分块打表,实现比较劣在两个半小时里面没能跑出来,一种可能的方式是使用多线程打表会好些
Code Display
const int N=1e6+10;
int cnt_d,id1[N],id2[N],Div[100],mp[10000],k,tot;
ll n,m,R[N],g1[N],g3[N],bl;
bool fl[N];
int pri[N],cnt,s1[N],s3[N];
vector<int> js[20];
struct Poly{
ll a[18]; Poly(){memset(a,0,sizeof a);}
Poly operator+(const Poly &b)const{
Poly c;
for(int i=0;i<cnt_d;++i) c.a[i]=a[i]+b.a[i];
return c;
}
Poly operator-(const Poly &b)const{
Poly c;
for(int i=0;i<cnt_d;++i) c.a[i]=a[i]-b.a[i];
return c;
}
Poly operator *(const Poly &b)const{
Poly c;
for(int i=0;i<cnt_d;++i){
for(auto j:js[i]){
c.a[mp[Div[i]*Div[j]]]+=a[i]*b.a[j];
}
} return c;
}
Poly operator *(const ll &b)const{
Poly c;
for(int i=0;i<cnt_d;++i) c.a[i]=a[i]*b;
return c;
}
}one,three,zero;
inline int get_id(ll x){
if(x>m) return -1;
return mp[x];
}
inline int id(ll x){return x<=bl?id1[x]:id2[n/x];}
inline Poly calc(ll n,int x){
if(n<pri[x]) return zero; Poly res=one*(g3[id(n)]-s3[x-1])+three*(g1[id(n)]-s1[x-1]);
/*
according to the formula of Min25 algorithm:
variable res should be initalized with this value
*/
for(int j=x;1ll*pri[j]*pri[j]<=n;++j){
int tim=1; ll prd=pri[j];
while(prd<=n){
Poly ret=calc(n/prd,j+1);
if(tim>1) ret=ret+one;
if(pri[j]%4==1){
Poly fpow;
int vv=get_id(2*tim+1);
if(~vv) fpow.a[vv]=1,res=res+fpow*ret;
}else res=res+one*ret;
tim++; prd*=pri[j];
}
}
return res;
}
signed main(){
// freopen("1.in","r",stdin);
freopen("jozb.in","r",stdin);
freopen("jozb.out","w",stdout);
n=2e5;
for(int i=2;i<=n;++i){
if(!fl[i]){
pri[++cnt]=i; s1[cnt]=s1[cnt-1]; s3[cnt]=s3[cnt-1];
if(i%4==3) s3[cnt]++;
if(i%4==1) s1[cnt]++;
}
for(int j=1;j<=cnt&&1ll*i*pri[j]<N;++j){
fl[i*pri[j]]=1; if(i%pri[j]==0) break;
}
}
memset(mp,-1,sizeof(mp));
n=read(); m=read(); bl=sqrt(n);
if(m%4||m>8748) puts("0"),exit(0); m/=4;
for(int i=1;i<=m;++i) if(m%i==0) Div[cnt_d]=i,mp[i]=cnt_d++;
one.a[0]=1; int ee=get_id(3); if(~ee) three.a[ee]=1;
for(int i=0;i<cnt_d;++i){
for(int j=0;j<cnt_d;++j) if(m%(Div[i]*Div[j])==0) js[i].pb(j);
}
if(cnt_d>18) puts("0"),exit(0);
for(ll l=1,r;l<=n;l=R[tot]+1){
R[++tot]=n/(n/l);
g1[tot]=(R[tot]-1)/4;
g3[tot]=(R[tot]+1)/4;
if(R[tot]<=bl) id1[R[tot]]=tot; else id2[n/R[tot]]=tot;
}
rep(i,2,cnt){
if(1ll*pri[i]*pri[i]>n) break;
if(pri[i]%4==1){
for(int j=tot;1ll*pri[i]*pri[i]<=R[j];--j){
int x=id(R[j]/pri[i]);
g1[j]-=g1[x]-s1[i-1];
g3[j]-=g3[x]-s3[i-1];
}
}else{
for(int j=tot;1ll*pri[i]*pri[i]<=R[j];--j){
int x=id(R[j]/pri[i]);
g1[j]-=g3[x]-s3[i-1];
g3[j]-=g1[x]-s1[i-1];
}
}
}
Poly res=calc(n,1);
print(res.a[mp[m]]+(m==1)*2);
return 0;
}