Farey Polygon


题目描述:

先给出一个法雷序列:在法雷序列之前加上0/0;
F1 = {0⁄0, 0⁄1, 1⁄1}
F2 = {0⁄0, 0⁄1, 1⁄2, 1⁄1}
F3 = {0⁄0, 0⁄1, 1⁄3, 1⁄2, 2⁄3, 1⁄1}
F4 = {0⁄0, 0⁄1, 1⁄4, 1⁄3, 1⁄2, 2⁄3, 3⁄4, 1⁄1}
F5 = {0⁄0, 0⁄1, 1⁄5, 1⁄4, 1⁄3, 2⁄5, 1⁄2, 3⁄5, 2⁄3, 3⁄4, 4⁄5, 1⁄1}
F6 = {0⁄0, 0⁄1, 1⁄6, 1⁄5, 1⁄4, 1⁄3, 2⁄5, 1⁄2, 3⁄5, 2⁄3, 3⁄4, 4⁄5, 5⁄6, 1⁄1}
F7 = {0⁄0, 0⁄1, 1⁄7, 1⁄6, 1⁄5, 1⁄4, 2⁄7, 1⁄3, 2⁄5, 3⁄7, 1⁄2, 4⁄7, 3⁄5, 2⁄3, 5⁄7, 3⁄4, 4⁄5, 5⁄6, 6⁄7, 1⁄1}
F8 = {0⁄0, 0⁄1, 1⁄8, 1⁄7, 1⁄6, 1⁄5, 1⁄4, 2⁄7, 1⁄3, 3⁄8, 2⁄5, 3⁄7, 1⁄2, 4⁄7, 3⁄5, 5⁄8, 2⁄3, 5⁄7, 3⁄4, 4⁄5, 5⁄6, 6⁄7, 7⁄8, 1⁄1}
然后将这些序列按照顺序映射到二维的坐标上.
分子是y,分母是x.按照顺序将他们连在一起,最后一个连第一个点.
然后这个图形里面就严格包含了一些点.
有一个倍率放大是m.就是把每一个点都放大.
现在给出严格包含了多少个点,要求给出满足的(n,m).n尽量小,n一样的话m尽量小.
12000组数据,要求n和m都<=15000,还没找到的话就是-1.
具体题意:http://acm.bnu.edu.cn/v3/problem_show.php?pid=20856

题解:

首先格子内部的点是有一个定理:在二维的方格中,围成的面积S = I(内部) + (V(边上)/2) - 1; 我们知道的是I(内部).
其次观察面积和边上点的性质.既然给出的是一个奇怪的序列.这个序列我们第一反应是可以用phi前缀和求出它的个数.那么我们猜想S和V是否只和点的个数有关.发现还真是:V = m*(点个数), S = (点个数-2)/2;
知道了这些,我们写出公式,发现m有二次方,那么我们暴力枚举m,然后可以得到需要的phi,这一点非常重要,因为得到phi中有一个需要整除的过程,能够特别快的加速计算.然后二分查找phi.

重点:

(1)知道算内部的公式.
(2)利用phi好求个数来猜想
(3)求phi是利用整除.

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(ll i = a;i < b;i++)
#define REP_D(i, a, b) for(ll i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const ll maxn = 15001 + 10;
const ll key = 15000;
ll phi[maxn];
ll p[maxn], pn, vis[maxn];
void getPhi()
{
    CLR(vis);
    phi[1] = 1;
    pn = 0;
    for(ll i = 2; i<=key; i++)
    {
        if(!vis[i])
        {
            phi[i] = i-1;
            p[pn] = i;
            pn++;
        }
        for(ll j = 0; (j<pn&&i*p[j]<=key); j++)
        {
            if(i%p[j])
            {
                phi[i*p[j]] = phi[i]*(p[j]-1);
                vis[i*p[j]] = 1;
            }
            else
            {
                phi[i*p[j]] = phi[i]*(p[j]);
                vis[i*p[j]] = 1;
            }
        }
    }
    for(int i = 2; i<=key; i++)
    {
        phi[i] += phi[i-1];
    }
}
ll n, m;
ll ans;
void solve()
{
    for(m = key; m>=2; m--)
    {
        ll X = (2*ans+2*m-2);
        ll Y = (m*m-m);
        if(X%Y==0)//这一点是关键.不然要T
        {
            X /= Y;
            ll pos = lower_bound(phi + 1, phi + 1+key, X) - phi;
            if(pos != (key + 1) && phi[pos] == X)
            {
                printf("%lld %lld\n", pos, m);
                return;
            }
        }
    }
    printf("NOT FOUND\n");
}

int main()
{
   // freopen("13Min.txt", "r", stdin);
    //freopen("1out.txt", "w", stdout);
    getPhi();
    while(scanf("%lld", &ans))
    {
        if(ans==-1)
        {
            break;
        }
        if(ans == 0)
        {
            printf("1 1\n");
        }
        else
            solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值