codeforces837E Vasya's Function

来源:互联网 发布:单片机引脚定义 编辑:程序博客网 时间:2024/05/20 14:19

首先在下参考了http://blog.csdn.net/calabash_boy/article/details/76652792
以及submit#29208538
数论什么的最讨厌了……


设f(x,y),若y=0,则f(x,0)=0,否则f(x,y)=f(x,y-gcd(x,y))。
数据范围是1<=x,y<=1012所以不能指望暴力(没错,3点就会T)
那么来分析一下。
不妨设x=A·gcd(x,y),y=B·gcd(x,y)。那么,一次操作后,x=A·gcd(x,y),y=(B-1)·gcd(x,y),显然gcd(x,y)是两个的因数之一。现在我们进行k次操作,x=A·gcd(x,y)不变,y=(B-k)·gcd(x,y),假设此时恰好gcd(A·gcd(x,y),(B-k)·gcd(x,y))改变,就意味着:gcd(A,B-k)不为1.
现在的问题就是如何求出k,因为如果知道k,我们就可以很快速的求出操作的次数。如果枚举的话一定会T,我们来稍微变个形:假定r为A的一个因子,那么(B-k)%r=0.不妨设(B-k)=q*r,则B=q*r+k,即B%r=k%r.因为k一定小于r,因为如果k大于等于r,一定存在k-r使(B-(k-r))%r=0,这与k最小相矛盾。所以,B%r=k。我们只需要在O(n)的时间内枚举出所有的因子,然后跑就可以。
这样的话思路的确是正确的,不过12点就T了。仔细想了一下,这样写会反复调用查找因子的函数,效率会非常低。那如何改进呢?
我研究了半天别人的代码,发现了一个非常巧妙的办法。首先我们先让x,y除gcd(x,y),此时的x的所有因子没有公共因子。那么我们就可以只枚举出此时x所有素数因子。
而由于x为定值,我们就可以在每一次操作之后的筛选中从已经筛选过的因子中再次筛选,如果某个因子是操作后y的因子,那么就不列为下次操作的考虑对象。否则就让y除以这些因子,因为这就相当于让y除以gcd(x,y).

#include <iostream>#include <vector>#include <algorithm>#define MAXN 1e14typedef long long LL;using namespace std;LL x,y,g,t;vector<LL> va;vector<LL>::iterator it;LL gcd(LL x,LL y){    return y==0?x:gcd(y,x%y);}LL init(){    g=gcd(x,y);    x=x/g,y=y/g;    for(LL i=2;i*i<=x;++i){        while(x%i==0){            va.push_back(i);            x/=i;        }                       }    if(x>1) va.push_back(x);}void work(LL y){    if(y==0) return;    LL k=y;    for(it=va.begin();it!=va.end();++it){        k=min(k,y%(*it));    }       t+=k,y-=k;    vector<LL> vb;    for(it=va.begin();it!=va.end();++it){        if(y%(*it)!=0) vb.push_back(*it);        else y/=(*it);    }    swap(va,vb);    work(y);}int main(){    cin>>x>>y;    init();    work(y);    cout<<t;}

数论太难了orz……

原创粉丝点击