zoukankan      html  css  js  c++  java
  • 备战noip week1

    Pairs of integers

    题意:给出一个整数N(N<=1e9)求两个整数X,Y使得X+Y=N,求所有的解
    其中Y是X直接去掉一位后得到的(1034->134,100->00.etc)
    题解:对于整数X,我们枚举它的每一位(第i位),并且枚举这一位上所有可能的数字d
    令第i位前的数字构成的数为x,第i位后的数字构成的数为y
    那么就有(x*10^i+d*10^{i-1}+y+x*10^{i-1}+y=N)
    因此(11*10^{i-1}+2y=N-d*10^{i-1})
    解不定方程即可,然后搞出所有解就行了
    用map实现本题十分方便(既可以去重也可以排序)
    P.S.

    • 本题的输出十分有意思
    • 我觉得可以不用代码里的倍增吧,得到最小的非负整数解很容易啊

    code:

    #include<cstdio>
    #include<map>
    using namespace std;
    typedef long long ll;
    ll N,P10[15];
    map<ll,ll>ans;
    void exgcd(ll a,ll b,ll &g,ll &x,ll &y)
    {
    	if(!b){x=1;y=0;g=a;return;}
    	exgcd(b,a%b,g,x,y);
    	ll xx=x;
    	x=y;y=xx-a/b*y;
    }
    inline int ws(ll x)
    {
    	if(!x)return 1;
    	int anss=0;
    	for(;P10[anss]<=x;++anss);
    	return anss;
    }
    int main()
    {
    	P10[0]=1;
    	for(int i=1;i<=10;++i)P10[i]=P10[i-1]*10ll;
    	while(scanf("%lld",&N)!=EOF)
    	{
    		ans.clear();
    		int len=ws(N);
    		if(N%P10[len-1]==0)ans[N]=0;
    		for(int i=1;i<=len;++i)
    		{
    			ll a=11*P10[i-1],b=2,g=0,x=0,y=0;
    			exgcd(a,b,g,x,y);
    			ll xx=x,yy=y;
    			for(int d=0;d<10;++d)
    			{
    				x=xx,y=yy;
    				ll c=N-1ll*d*P10[i-1];
    				if(c%g)continue;
    				ll s=c/g,_a=0,_b=0;
    				x*=s,y*=s;_a=a/g,_b=b/g;
    				y=(y%_a+_a)%_a;
    				for(;y<P10[i-1];y+=_a)
    				{
    					x=(c-y*b)/a;
    					ll X=x*P10[i]+d*P10[i-1]+y,Y=x*P10[i-1]+y;
    					if(X!=Y&&X>=0&&Y>=0)ans[X]=Y;
    				}
    			}
    		}
    		printf("%lld
    ",ans.size());
    		for(map<ll,ll>::iterator it=ans.begin();it!=ans.end();it++)
    			printf("%lld + %0*lld = %lld
    ",it->first,ws(it->first)-1,it->second,N);
    	}
    	return 0;
    }
    

    最小公倍数的最小和

    注意细节:
    1.n=1
    2.n为素数
    3.n=2^31-1(答案会越界)

    #include<bits/stdc++.h>
    using namespace std;
    int n,kase;
    int main()
    {
    	while(scanf("%d",&n)==1&&n)
    	{
    		long long ans=0;int nn=n,mx=(int)sqrt(n);
    		for(int i=2;i<=mx;++i)
    		{
    			int cnt=1;
    			while(n%i==0)n/=i,cnt*=i;
    			if(cnt!=1)ans+=1ll*cnt;
    			if(n==1)break;
    		}
    		if(ans==0)ans=1ll*nn+1;
    		else if(n!=1)ans+=1ll*n;
    		if(ans==1ll*nn)++ans;
    		if(nn==1)ans=2;
    		printf("Case %d: %lld
    ",++kase,ans);
    	}
    	return 0;
    }
    

    选择与除法

    质因数分解。统计指数就可以了

    #include<bits/stdc++.h>
    using namespace std;
    const int N=10005;
    int pr[N],p,q,r,s;
    double ans;
    inline int cp(int &x,int d)
    {
    	int cnt=0;
    	while(x%d==0)x/=d,++cnt;
    	return cnt;
    }
    inline void work(int x,int d)
    {
    	int mx=(int)sqrt(x+1);
    	for(int i=2;i<=mx&&x!=1;++i)
    		if(x%i==0)pr[i]+=d*cp(x,i);
    	if(x>1)pr[x]+=d;
    }
    inline void ad(int x,int d)
    {
    	for(int i=2;i<=x;++i)
    		work(i,d);
    }
    inline void solve()
    {
    	ans=1;
    	for(int i=2;i<=N-5;++i)
    		ans*=pow(i,pr[i]),pr[i]=0;
    	printf("%.5lf
    ",ans);
    }
    int main()
    {
    	while(scanf("%d%d%d%d",&p,&q,&r,&s)==4)
    	{
    		ad(p,1);ad(q,-1);
    		ad(s,1);ad(r,-1);
    		ad(r-s,1);ad(p-q,-1);
    		solve();
    	}
    	return 0;
    }
    

    交表

    题意:求出整数对个数(x,y)(x,y<=n n<=5e4)使得其最大公约数为1
    题解:这样的整数对除(1,1)其他的x不等于y
    不妨设x>y(x<y同理)那么只要确定x,答案就是phi(x)
    筛出所有phi(x),求出前缀和即可
    code:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=50005;
    vector<int>pr;
    int phi[N],sm[N],n;
    bool flag[N];
    inline void pre()
    {
    	int m=N-5;
    	for(int i=2;i<=m;++i)
    	{
    		if(!flag[i]){phi[i]=i-1;pr.push_back(i);}
    		for(int j=0;j<pr.size();++j)
    		{
    			if(i*pr[j]>m)break;
    			flag[i*pr[j]]=true;
    			if(i%pr[j])phi[i*pr[j]]=phi[i]*phi[pr[j]];
    			else{phi[i*pr[j]]=phi[i]*pr[j];break;}
    		}
    	}
    	for(int i=2;i<=m;++i)
    		sm[i]=sm[i-1]+phi[i];
    }
    int main()
    {
    	pre();
    	while(scanf("%d",&n)==1&&n)
    		printf("%d
    ",sm[n]*2+1);
    	return 0;
    }
    

    树林里的树

    题解:
    可以发现坐标轴上有且仅有4个点可以看到,单独考虑
    发现只用考虑右上角的点就行了(其他方位同理)
    由于列数较少行数较多,因此枚举列数
    对于列x,能看到的当且仅当其纵坐标与x互质
    不难想到欧拉函数
    稍稍扩展可以证明(kx+1到(k+1)x)(其中k为正整数)中与x互质的数的个数仍然为phi(x)
    简略证明:
    若d(d<x)与x互质,那么d+x仍然与x互质
    若e(e<=x)与x不互质,那么e+x仍然与x不互质
    由此可以快速计算每列可以看到多少树
    至于余数部分,暴力计算统计即可

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=2005;
    vector<int>p;
    int lp[N],phi[N],a,b;
    bool flag[N];
    int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
    inline void pre()
    {
    	int m=N-5;phi[1]=1;
    	for(int i=2;i<=m;++i)
    	{
    		if(!flag[i]){phi[i]=i-1;p.push_back(i);}
    		for(int j=0;j<p.size();++j)
    		{
    			if(i*p[j]>m)break;
    			flag[i*p[j]]=true;
    			if(i%p[j])phi[i*p[j]]=phi[i]*phi[p[j]];
    			else{phi[i*p[j]]=phi[i]*p[j];break;}
    		}
    	}
    }
    int main()
    {
    	pre();
    	while(scanf("%d%d",&a,&b)==2&&a&&b)
    	{
    		ll ans=0,t=0;
    		for(int i=1;i<=a;++i)
    		{
    			int d=b/i;
    			ans+=1ll*d*phi[i];
    			for(int j=d*i+1;j<=b;++j)
    				if(gcd(j,i)==1)++ans;
    		}
    		ans*=4;ans+=4;t=1ll*(a*2+1)*(b*2+1)-1;
    		printf("%.10lf
    ",(double)ans/t);
    	}
    	return 0;
    }
    

    数论难题

    题解:
    由题意可以知道枚举每个模数的余数,然后直接上中国剩余定理即可
    但是倘若总的可能情况过大,这样可能超时
    可以这样解决:
    首先找到一个i使得(K_i/X_i)(K_i)(X_i)如题目所述)尽量小
    至于为何如此选择,我认为原因有二:

    • 让可能的解分布尽可能稀疏,减少判断次数
    • 对于剩下来的其他条件,由于最难满足的已经被剔除(就是之前选出来的),可能解更有可能成为真正的解

    以这个基数为基础,暴力枚举判断其他模数下是否满足条件输出即可
    因为总的可能情况很大,因此这样做复杂度可以保证
    code:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef set<long long>::iterator si;
    ll C,S,BASE,X[15],K[15];
    set<ll>Y[15];
    inline ll read(){ll x;scanf("%lld",&x);return x;}
    inline ll pre()
    {
    	ll M=1;BASE=0;
    	for(ll i=1;i<=C;++i)
    	{
    		scanf("%lld%lld",&X[i],&K[i]);
    		M*=K[i];Y[i].clear();
    		for(ll j=1;j<=K[i];++j)
    			Y[i].insert(read());
    		if(!BASE||K[BASE]*X[i]>K[i]*X[BASE])BASE=i;
    	}
    	return M;
    }
    vector<ll>Ans;
    ll a[15];
    void exgcd(ll a,ll b,ll &x,ll &y)
    {
    	if(!b){x=1,y=0;return;}
    	exgcd(b,a%b,y,x);y-=a/b*x;
    }
    inline ll china()
    {
    	ll M=1,ans=0;
    	for(ll i=1;i<=C;++i)M*=X[i];
    	for(ll i=1;i<=C;++i)
    	{
    		ll w=M/X[i],x=0,y=0;
    		exgcd(X[i],w,x,y);
    		ans=(ans+w*y%M*a[i]%M)%M;
    	}
    	return (ans+M)%M;
    }
    void dfs(ll p)
    {
    	if(p==C+1){Ans.push_back(china());return;}
    	for(si it=Y[p].begin();it!=Y[p].end();it++)
    		a[p]=*it,dfs(p+1);
    }
    inline void work1()
    {
    	Ans.clear();
    	dfs(1);
    	ll M=1;
    	for(ll i=1;i<=C;++i)M*=X[i];
    	sort(Ans.begin(),Ans.end());
    	for(ll i=0;S;++i)
    		for(size_t j=0;j<Ans.size();++j)
    		{
    			ll x=i*M+Ans[j];
    			if(x<=0)continue;
    			printf("%lld
    ",x);
    			if(--S==0)break;
    		}
    }
    inline void work2()
    {
    	for(ll i=0;S;++i)
    		for(si it=Y[BASE].begin();it!=Y[BASE].end();it++)
    		{
    			ll x=i*X[BASE]+*it;
    			if(x<=0)continue;
    			bool flag=false;
    			for(ll k=1;k<=C;++k)
    			{
    				if(k==BASE)continue;
    				if(!Y[k].count(x%X[k]))
    				{
    					flag=true;break;
    				}
    			}
    			if(flag)continue;
    			printf("%lld
    ",x);
    			if(--S==0)break;
    		}
    }
    int main()
    {
    	while(scanf("%lld%lld",&C,&S)==2&&C&&S)
    		pre()<=10000ll?work1():work2(),puts("");
    	return 0;
    }
    

    帮帮Tomisu

    题意:(2-N!)中有多少个数满足它们的所有质因子都大于(M)其中(N>=M)
    题解:考察phi(M!)表示小于M!中与M!互质的数的个数
    这些数一定满足它们的所有质因子都大于M(否则就和M!不互质了)
    根据树林里的树这道题,那么最终的答案就是N!/M!*phi(M!)
    那么如何求(phi(M!))呢?
    考虑递推。假设我们已求得(phi(i!))要求(phi((i+1)!))
    由欧拉函数通式

    (varphi)((x)=xprod_{i=1}^n(1-frac{1}{p_i}))
    其中(p_1), (p_2)……(p_n)(x)的所有质因数,(x)是不为(0)的整数

    因此如果i+1为素数,则(phi((i+1)!)=(i+1)*phi(i!)/(i+1)*i)
    如果i+1不是素数,则(phi((i+1)!)=(i+1)*phi(i!))(因为i+1的所有质因数都被包含在i!中)

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=100000007,M=1e7+5;
    int n,m,fac[M];
    vector<int>p;
    bitset<M>flag;
    inline void pre()
    {
    	int m=M-5;
    	for(int i=2;i<=m;++i)
    	{
    		if(!flag[i])p.push_back(i);
    		for(size_t j=0;j<p.size();++j)
    		{
    			int num=p[j]*i;
    			if(num>m)break;
    			flag[num]=true;
    			if(i%p[j]==0)break;
    		}
    	}
    	fac[1]=1;
    	for(int i=2;i<=m;++i)
    		fac[i]=1ll*fac[i-1]*(flag[i]?i:i-1)%mod;
    }
    int main()
    {
    	pre();
    	while(scanf("%d%d",&n,&m)==2&&n&&m)
    	{
    		int ans=fac[m];
    		for(int i=m+1;i<=n;++i)
    			ans=1ll*ans*i%mod;
    		printf("%d
    ",ans-1);
    	}
    	return 0;
    }
    

    约瑟夫的数论问题/余数求和

    题解:
    (sum_{i=1}^{n} k mod i)
    (=sum_{i=1}^{n}(k- lfloorfrac{k}{i} floor *i))
    (=k*n-sum_{i=1}^{n}lfloorfrac{k}{i} floor *i)
    后面那一部分可以用整除分块计算
    注意细节

    #include<cstdio>
    #include<iostream>
    using namespace std;
    typedef long long ll;
    int n,k;
    ll ans;
    int main()
    {
    	scanf("%d%d",&n,&k);
    	ans=1ll*n*k;
    	for(ll l=1,r;l<=k&&l<=n;l=r+1)
    	{
    		r=min(k/(k/l),1ll*n);
    		ans-=(l+r)*(r-l+1)/2*(k/l);
    	}
    	cout<<ans;
    	return 0;
    }
    

    (LOJ10038 A Horrible Poem,POI2012)[https://loj.ac/problem/10038]

    题意:给出一个长度为n的字符串S(n<=5e5),接下来有q(q<=2e6)个询问
    每个询问输入两个数a,b,求S[a,b]内最短循环节长度(每个循环节都是完整的)
    题解:
    本题十分有趣~~
    考虑对于每一个询问,枚举最短循环节长度进行判断
    根据套路,可以先用哈希进行预处理,然后比较border(这样可以做到O(1)判断)

    inline bool judge(int l,int r,int len){return get_hash(l,r-len)==get_hash(l+len,r);}
    //判断l到r的区间中长度为len的循环节是否存在
    

    而后容易想到循环节长度一定是区间长度的约数
    枚举约数判断
    但是这样仍然不行
    如何优化呢?
    不妨假设最短循环节长度为len,循环了k次
    对于任意(d|k,len imes d)也一定是循环节长度
    因此只要(k!=1)我们总是能找到一个素数p满足p|k
    此时(len imes k/p)是循环节长度,不断迭代即可
    意即我们不用枚举所有约数,枚举素因子足矣
    但这样就要求快速获得一个数的质因数分解
    上线性筛就可以了

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e5+5,mod=91203991;
    char ch[N];
    int n,base=31,p[N],hash[N],lp[N];
    bool flag[N];
    vector<int>pm;
    inline int read()
    {
    	int s=0,w=1; char ch=getchar();
    	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
    	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
    	return s*w;
    }
    inline void pre1()
    {
    	p[0]=1;hash[0]=1;
    	for(int i=1;i<=n;++i)
    	{
    		p[i]=1ll*p[i-1]*base%mod;
    		hash[i]=(1ll*hash[i-1]*base%mod+ch[i]-'a'+1)%mod;
    	}
    }
    inline void pre2()
    {
    	lp[1]=1;
    	for(int i=2;i<=n;++i)
    	{
    		if(!flag[i]){lp[i]=i;pm.push_back(i);}
    		for(size_t j=0;j<pm.size();++j)
    		{
    			int num=pm[j]*i;
    			if(num>n)break;
    			lp[num]=pm[j];flag[num]=true;
    			if(i%pm[j]==0)break;
    		}
    	}
    }
    inline int get_hash(int l,int r){return ((hash[r]-1ll*hash[l-1]*p[r-l+1]%mod)%mod+mod)%mod;}
    inline bool judge(int l,int r,int len){return get_hash(l,r-len)==get_hash(l+len,r);}
    inline int get_ans(int l,int r)
    {
    	int len=r-l+1;
    	if(judge(l,r,1))return 1;
    	vector<int>fac;
    	while(len>1)
    	{
    		fac.push_back(lp[len]);
    		len/=lp[len];
    	}
    	len=r-l+1;
    	for(size_t i=0;i<fac.size();++i)
    		if(judge(l,r,len/fac[i]))len/=fac[i];
    	return len;
    }
    int main()
    {
    	n=read();
    	scanf("%s",ch+1);
    	pre1();pre2();
    	int q=read();
    	while(q--)
    	{
    		int l=read(),r=read();
    		printf("%d
    ",get_ans(l,r));
    	}
    	return 0;
    }
    
  • 相关阅读:
    算法初步-插入排序
    [数据结构]之栈
    [数据结构]之队列
    [数据结构]之链表
    [数据结构]之顺序表
    SDL一环境配置
    三大框架结构的原理及其优点
    hibernate----hibernate的基础设置
    其他
    struts----用Action的属性接收参数
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/13610400.html
Copyright © 2011-2022 走看看