2022/6/19 数学专场游记
->比赛链接
\(Vjudge\) 居然又不支持万能头……
A.Farey Sequence
- 
翻译后题意就是求 \(2\sim n\) 中小于每个数且与每个数互质的数的个数,即 \(\sum\limits_{i=2}^n \varphi(i)\); 
- 
所以直接线性筛法同时把 \(\varphi(i)\) 求了,再前缀和一下就结束了; 
AC code
#include<iostream>
#include<ios>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int n;
int st[N],p[N],k=0;
long long phi[N];
void pre(){
	phi[1]=phi[0]=0;
	for(int i=2;i<N;++i){
		if(!st[i]){
			p[++k]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=k && p[j]*i<N;++j){
			st[i*p[j]]=1;
			phi[i*p[j]]=1ll*((i%p[j]==0)?p[j]:phi[p[j]])*phi[i];
			if(i%p[j]==0){
				break;
			}
		}
	}
	for(int i=2;i<N;++i)
		phi[i]+=phi[i-1];
	return ;
}
int main(){
	pre();
	while(scanf("%d",&n)!=EOF){
		if(!n) break;
		printf("%lld\n",phi[n]);
	}
	return 0;
}
B.Fast Matrix Calculation
- 
矩阵乘法, 多亏了陈老最后二十分钟力挽狂澜,不然就没了;
- 
由于矩阵 \(C=A\times B\),所以 \(C\) 的大小其实是 \(n\times n\),显然会 \(TLE\); 
 对 \(C^{n^2}\) 展开,就会得到 \(ANS=(A\times B)\times(A\times B)\times...\times(A\times B)\),然后我们就会发现,不仅可以计算 \(A\times B\),也可以计算 \(B\times A\),而且这样产生的需要快速幂的矩阵大小就只有 \(k\times k\),瞬间小了很多;
 所以最后计算 \(A\times(B\times A)^{n^2-1}\times B\) 即可;
AC code
#include<iostream>
#include<ios>
#include<functional>
#include<utility>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1010;
int n,k;
struct memr{
	int x,y;
	int s[N][N];
	memr operator*(const memr &_)const{
		memr cnt;
		memset(cnt.s,0,sizeof(cnt.s));
		cnt.x=x,cnt.y=_.y;
		for(int i=1;i<=x;++i)
			for(int j=1;j<=_.y;++j)
				for(int k=1;k<=y;++k)
					(cnt.s[i][j]+=s[i][k]*_.s[k][j])%=6;
		return cnt;
	}
}a,b,c;
int main(){
	while(scanf("%d%d",&n,&k)!=EOF){
		if(!n && !k) break;
		a.x=n,a.y=k;
		b.x=k,b.y=n;
		for(int i=1;i<=n;++i)
			for(int j=1;j<=k;++j)
				scanf("%d",&a.s[i][j]);
		for(int i=1;i<=k;++i)
			for(int j=1;j<=n;++j)
				scanf("%d",&b.s[i][j]);
		c=b*a;
		memr cnt=c,dx=c;
		int t=n*n-2;
		for(;t;t>>=1,dx=dx*dx){
			if(t&1)
				cnt=cnt*dx;
		}
		cnt=a*cnt;
		cnt=cnt*b;
		long long ans=0;
		for(int i=1;i<=n;++i){
			for(int j=1;j<=n;++j){
				ans+=cnt.s[i][j];
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}
C.Sum
- 
容斥原理和 \(Mobius\) 函数; 
- 
求区间内与 \(x\) 互质的数的和,考虑筛去不与 \(x\) 互质的数; 
 先对 \(x\) 算数基本定理分解,由于 \(x\le 4\times 10^5\),所以 \(x\) 中至多含有 \(6\) 个不同质数;且求互质只与有哪些质数有关,与质数的具体次数无关;
 对于每个质因数 \(p_i\),区间 \(l\sim r\) 中最大的它的倍数为 \(\left\lfloor\dfrac{r}{p_i}\right\rfloor\times p_i\),最小的倍数为 \((\left\lfloor\dfrac{l-1}{p_i}\right\rfloor+1)\times p_i\),如果要求的是它们的和,则提取 \(p_i\) 为公因数,剩下的就是对 \((\left\lfloor\dfrac{l-1}{p_i}\right\rfloor+1)\sim \left\lfloor\dfrac{r}{p_i}\right\rfloor\) 这一段连续自然数求和,求和公式就可以搞定;
 对于其中有多个质数都是其因数的数,显然计算了多次,要再次加回来;
 用二进制来表示一个质数选或不选,则最多进行 \(2^6=64\) 次筛选,可以用 \(Mobius\) 函数来处理 \(+/-\) 的问题(直接求有几个质数也可以);
- 
对于被修改的数,先假设它们未被修改,在原数列上进行计算,然后再扫出 \(l\sim r\) 区间中被修改了的数; 
 设修改前为 \(a\),改后为 \(b\),则分为以下几种情况:- \(\gcd(a,x)=1\) 且 \(\gcd(b,x)=1\),说明无论是否修改这个值都在答案中,则 \(ans+=b-a\);
- \(\gcd(a,x)=1\) 且 \(\gcd(b,x)>1\),说明改后就没有这个值了,则 \(ans-=a\);
- \(\gcd(a,x)>1\) 且 \(\gcd(b,x)=1\),说明改前没有贡献,但改后就应被计算,则 \(ans+=b\);
- \(\gcd(a,x)>1\) 且 \(\gcd(b,x)>1\),说明改前改后这个值都没有贡献,不用操作;
 
- 
修改的时候可能出现同一位置的数被修改多次的情况; 
AC code
#include<iostream>
#include<ios>
#include<functional>
#include<utility>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}
const int N=4e5+10;
bool vis[N];
int c[N],id[N],t;
int n,m,T;
int st[N],p[N],mb[N],k=0;
#define ll long long
void pre(){
	for(int i=2;i<N;++i){
		if(!st[i]){
			p[++k]=i;
			mb[i]=-1;
		}
		for(int j=1;j<=k && p[j]*i<N;++j){
			st[i*p[j]]=1;
			if(i%p[j]==0){
				mb[i*p[j]]=0;
				break;
			}
			mb[i*p[j]]=mb[p[j]]*mb[i];
		}
	}
	return ;
}
int pz[15],tot=0;
int f(int x){
	int cnt=0;
	for(;x;x>>=1)
		if(x&1)
			++cnt;
	return cnt;
}
int g(int x){
	int cnt=1;
	for(int j=0;j<tot;++j)
		if((x>>j)&1)
			cnt*=pz[j+1];
	return cnt;
}
int gcd(int x,int y){
	return (y==0)?x:gcd(y,x%y);
}
void work(int l,int r,int x){
	tot=0;
	int ix=x;
	for(int i=1;i<=k && p[i]<=ix;++i){
		if(ix%p[i]==0)
			pz[++tot]=p[i];
		while(ix%p[i]==0)
			ix/=p[i];
	}
	ll ans=1ll*(l+r)*(r-l+1)/2;
//	cout<<ans<<endl;
	for(int i=1;i<(1<<(tot));++i){
		int dx=g(i);
		int dr=r/dx,dl=(l-1)/dx;
		ans+=0ll+mb[dx]*1ll*dx*(1ll*(dr+dl+1)*(dr-dl)/2);
//		cout<<dl<<" "<<dr<<" "<<dx<<" "<<ans<<endl;
	}
	for(int i=1;i<=t;++i){
		if(id[i]<l || id[i]>r) continue;
		if(abs(gcd(id[i],x))!=1){
			if(abs(gcd(c[i],x))==1){
				ans+=c[i];
			}
			else ;
		}
		else{
			if(abs(gcd(c[i],x))!=1){
				ans-=id[i];
			}
			else{
				ans+=c[i]-id[i];
			}
		}
	}
	printf("%lld\n",ans);
	return ;
}
int main(){
//	freopen("in.in","r",stdin);
//	freopen("out.out","w",stdout);
	pre();
	T=read();
	while(T--){
		memset(vis,0,sizeof(vis));
		t=0;
		n=read(),m=read();
		int opt,x,y,z;
		while(m--){
			opt=read(),x=read(),y=read();
			if(opt==1){
				z=read();
				work(x,y,z);
			}
			else{
				if(vis[x]){
					for(int i=1;i<=t;++i){
						if(id[i]==x){
							c[i]=y;
							break;
						}
					}
				}
				else{
					vis[x]=1;
					c[++t]=y;
					id[t]=x;	
				}
			}	
		}
	}
	return 0;
}
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号