zoukankan      html  css  js  c++  java
  • Atcoder Grand Contest 015 F Kenus the Ancient Greek(找性质+乱搞)

    洛谷题面传送门 & Atcoder 题面传送门

    一道难度 Au 的 AGC F,虽然看过题解之后感觉并不复杂,但放在现场确实挺有挑战性的。

    首先第一问很简单,只要每次尽量让“辗转相除”变为“辗转相减”即可,具体构造就是 \((F_k,F_{k+1})\),其中 \(F_i\) 为斐波那契数列第 \(i\) 项,\(F_0=F_1=1\),也就是说最终的答案即为最大的 \(k\) 满足 \(F_{k-1}\le X\)\(F_k\le Y\)(不妨假设 \(X\le Y\))。

    接下来考虑第二问,我们记 \(A(i,j)\) 表示 \(i,j\) 辗转相除法求 gcd 的次数,\(B(i,j)\) 表示 \(X=i,Y=j\) 时的答案,那么我们显然要求的是 \(A(i,j)=B(i,j)=A(X,Y)\)\(i\le X,j\le Y\)\((i,j)\) 对数,不难发现这个对数可能很多,但我们还可以发现对于某个 \(A(i,j)=B(i,j),i<j\) 的二元组 \((i,j)\),一定有 \(A(i,j+ki)=B(i,j+ki),k\in\mathbb{Z}\),因此我们只用考虑 \(A(i,j)=B(i,j)\)\(i<j\le 2i\) 的二元组 \((i,j)\) 即可,打个表即可发现这样的二元组个数不是太多,具体来说,满足 \(A(i,j)=B(i,j)=k,i<j\le 2i\) 的二元组 \((i,j)\) 总共只有 \(k-1\) 个,因此我们考虑这样一个思路:对每个 \(k\) 预处理出满足 \(A(i,j)=B(i,j)=k,i<j\le 2i\) 的所有二元组,然后每次询问暴力枚举每个这样的二元组统计答案,于是现在我们只需考虑怎样求出二元组即可。

    首先 \(k=2\) 时候显然只有一个二元组 \((1,2)\),我们考虑已经求出了 \(k-1\) 时所有符合条件的二元组 \((a,b)\),那么显然 \(A(b,a+b)=A(a,b)+1\),而根据 \(a<b\le 2a\) 可知 \(a+b<2b\),因此 \(b<a+b<2b\),故 \((b,a+b)\) 为符合条件的二元组,又因为对于所有互不相同的二元组 \((a,b)\),它扩展得到的 \((b,a+b)\) 也两两不同,因此得到的 \(k-2\) 个二元组也两两互不相同。但是这样还会漏掉一个二元组,不难发现 \((F_{k+1}+F_{k-1},F_{k+1})\) 也是符合条件的二元组并且没有被我们计算,把它算上就不重不漏刚好 \(k-1\) 个了。

    时间复杂度 \(\log^2A+q\log A\)。注意对于答案等于 \(1\) 的情形,所有形如 \((i,i)(i\in[1,\min(X,Y)])\) 的二元组也符合条件,因此答案要加上 \(\min(X,Y)\)

    const int MAX=100;
    const int MOD=1e9+7;
    const ll MAXV=1e18;
    ll fib[MAX+5];int cnt=1;
    vector<pair<ll,ll> > prs[MAX+5];
    int main(){
    	fib[0]=fib[1]=1;
    	while(fib[cnt]+fib[cnt-1]<=MAXV) fib[cnt+1]=fib[cnt]+fib[cnt-1],++cnt;
    	prs[1].pb(mp(1,2));
    	for(int i=2;i<=cnt;i++){
    		for(pair<ll,ll> p:prs[i-1]) prs[i].pb(mp(p.se,p.fi+p.se));
    		prs[i].pb(mp(fib[i+1],fib[i-1]+fib[i+1]));
    	} int qu;scanf("%d",&qu);
    	while(qu--){
    		ll x,y,ans=0;scanf("%lld%lld",&x,&y);
    		int cur=1;if(x>y) x^=y^=x^=y;
    		while(cur+2<=cnt&&fib[cur+1]<=x&&fib[cur+2]<=y) ++cur;
    		for(pair<ll,ll> p:prs[cur]){
    			if(p.fi<=x&&p.se<=y) ans+=(y-p.se)/p.fi+1;
    			if(p.se<=x) ans+=(x-p.se)/p.fi+1;ans%=MOD;
    		} if(cur==1) (ans+=x)%=MOD; 
    		printf("%d %lld\n",cur,ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    oracle使用expdp备份数据库
    用Setuptools构建和分发程序包
    C#5.0-原生异步编程方式
    任务并行库
    线程-线程池1
    多线程-3(同步)
    多线程-2(线程同步)
    线程---1
    高性能-GC3
    高性能-GC2
  • 原文地址:https://www.cnblogs.com/ET2006/p/agc015F.html
Copyright © 2011-2022 走看看