UVa10213 多少块土地

UVa10213 多少块土地

有一块椭圆形的地。在边界上选 n ( 0 ≤ n< 2 31 2^{31} 231 )个点并两两连接得到 n(n-1)/2 条线段。它们最多能把地分成多少个部分?

样例:

Sample Input
4
1
2
3
4
Sample Output
1
2
4
8

如图所示:

根据拓扑学的欧拉公式, V-E+F=2 ,式中, V 是顶点数, E 是边数, F 是面数。展开在平面图中, F 包含最外层的“无限面”,欧拉公式仍然成立。

首先枚举一条从固定点出发的对角线,例如枚举固定点 A ,对角线 AD ,则它的左边有 BC 2 个点,右边有 EF 2 个点,左右点之间的连线有 2*2 个。那这些连线在对角线 AD 上形成的交点最多有 2*2 个( G 是其中之一), 2*2 个交点和 AD 两点把线段 AD 切分成了 2*2+1 段( GH 是其中一段)。我们要注意 G 在以 A 为固定点 AD 为对角线、以 D 为固定点 AD 为对角线、以 B 为固定点 BF 为对角线、以 F 为固定点 BF 为对角线这 4 中情况重复计数了,而边 GH 在以 A 为固定点 AD 为对角线、以 D 为固定点 AD 为对角线这 2 种情况重复计数了。

所以枚举一条从固定点(一共有 n 个固定点,所以最后要乘 n )出发的对角线,它的左边有 i 个点,右边有 n-2-i 个点。左右点的连线在这条对角线上形成 i ( n − 2 − i ) i(n-2-i) i(n2i) 个交点,得到 i ( n − 2 − i ) + 1 i(n-2-i)+1 i(n2i)+1 条线段。每个交点被重复计算 4 次,每条线段被重复计算 2 次。
V = n + n 4 ∑ i = 0 n − 2 i ( n − 2 − i ) V=n+\frac n 4\sum_{i=0}^{n-2}i(n-2-i) V=n+4ni=0n2i(n2i)
E = n + n 2 ∑ i = 0 n − 2 ( i ( n − 2 − i ) + 1 ) E=n+\frac n 2\sum_{i=0}^{n-2}(i(n-2-i)+1) E=n+2ni=0n2(i(n2i)+1)
根据欧拉公式(减去 1 是因为最外层的土地不算):
F = E − V + 2 − 1 = n ( n − 1 ) 2 + n 4 ∑ i = 0 n − 2 i ( n − 2 − i ) + 1 F=E-V+2-1=\frac {n(n-1)} 2+\frac n 4\sum_{i=0}^{n-2}i(n-2-i)+1 F=EV+21=2n(n1)+4ni=0n2i(n2i)+1

这样程序的复杂度就是 O(n),最多 3500 道题, n 最大 2 31 2^{31} 231 3500 × 2 31 &gt; 7 × 1 0 12 3500\times 2^{31}&gt;7\times10^{12} 3500×231>7×1012,如果每秒 3 亿次运算,需要计算五个小时。明显超时。

此处重点在于 ∑ \sum 运算, ∑ i ( n + 1 − i ) = n ( n + 1 ) ( n + 2 ) 6 \sum i(n+1-i)=\frac {n(n+1)(n+2)} 6 i(n+1i)=6n(n+1)(n+2) ,该公式可以使用数学归纳法证明(证明过程在下文中)。
所以,

F = n ( n − 1 ) 2 + n ( n − 1 ) ( n − 2 ) ( n − 3 ) 24 + 1 F=\frac {n(n-1)} 2+\frac {n(n-1)(n-2)(n-3)} {24}+1 F=2n(n1)+24n(n1)(n2)(n3)+1

相关公式证明

a n = ∑ i ( n + 1 − i ) a_n=\sum i(n+1-i) an=i(n+1i)
注意 ∑ i ( n + 1 − i ) = ∑ i = 0 n + 1 i ( n + 1 − i ) = ∑ i = 1 n i ( n + 1 − i ) \sum i(n+1-i)=\sum_{i=0}^{n+1} i(n+1-i)=\sum_{i=1}^{n} i(n+1-i) i(n+1i)=i=0n+1i(n+1i)=i=1ni(n+1i)

  1. n=1 时, a 1 = 1 × 1 = 1 × 2 × 3 6 a_1=1\times 1=\frac {1\times2\times3} 6 a1=1×1=61×2×3 ,等式成立。
  2. 假设 n=k 时, a k = ∑ i ( k + 1 − i ) = k ( k + 1 ) ( k + 2 ) 6 a_k=\sum i(k+1-i)=\frac {k(k+1)(k+2)} 6 ak=i(k+1i)=6k(k+1)(k+2)
    式子左边展开为 1 × k + 2 × ( k − 1 ) + 3 × ( k − 2 ) + . . . + k × 1 1\times k+2\times(k-1)+3\times(k-2)+...+k\times 1 1×k+2×(k1)+3×(k2)+...+k×1
  3. 则 n=k+1 时,

a k + 1 = ∑ i ( k + 1 + 1 − i )                                                                                    = 1 × ( k + 1 ) + 2 × k + 3 × ( k − 1 ) + 4 × ( k − 2 ) + . . . + k × 2 + ( k + 1 ) × 1 a_{k+1}=\sum i(k+1+1-i) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\\~~~~~=1\times (k+1)+2\times k+3\times(k-1)+4\times(k-2)+...+k\times 2+(k+1)\times 1 ak+1=i(k+1+1i)                                                                                 =1×(k+1)+2×k+3×(k1)+4×(k2)+...+k×2+(k+1)×1
下面我们来看 a k a_k ak a k + 1 a_{k+1} ak+1 的变化:

相减之后可以看到是一个等差数列,根据等差数列求和公式可以知道:
a k + 1 − a k = 1 + 2 + 3 + . . . + k + ( k + 1 ) = ( k + 1 ) ( k + 2 ) 2 a_{k+1}-a_k=1+2+3+...+k+(k+1)=\frac {(k+1)(k+2)} 2 ak+1ak=1+2+3+...+k+(k+1)=2(k+1)(k+2)
∴ a k + 1 = k ( k + 1 ) ( k + 2 ) 6 + ( k + 1 ) ( k + 2 ) 2 = ( k + 1 ) ( k + 2 ) ( k + 3 ) 6 = [ k + 1 ] ( [ k + 1 ] + 1 ) ( [ k + 1 ] + 2 ) 6 \therefore a_{k+1}=\frac {k(k+1)(k+2)} 6+\frac {(k+1)(k+2)} 2\\=\frac {(k+1)(k+2)(k+3)} 6\\=\frac {[k+1]([k+1]+1)([k+1]+2)} 6 ak+1=6k(k+1)(k+2)+2(k+1)(k+2)=6(k+1)(k+2)(k+3)=6[k+1]([k+1]+1)([k+1]+2)
证明完毕。

完整代码

代码中的 BigInteger 使用了 zypang1[中等 ] 比较完整的 BigInteger 高精度整数类( C++实现) 的代码。

//#define LOCAL
#include <iostream>
#include <cstdio>
#include <vector>

using namespace std;
struct BigInteger
{
    vector<int> s;
    static const int BASE = 10000;
    static const int WIDTH = 4;
    void standardize()
    {
        for (int i = s.size() - 1; i >= 0; --i)
        {
            if (s[i] == 0)
                s.pop_back();
            else
                break;
        }
        if (s.empty())
            s.push_back(0);
    }

    BigInteger &operator=(long long num)
    {
        s.clear();
        do
        {
            s.push_back(num % BASE);
            num /= BASE;
        } while (num > 0);
        return *this;
    }
    BigInteger &operator=(const string &num)
    {
        s.clear();
        int len = (num.size() - 1) / WIDTH + 1;
        int x = 0;
        for (int i = 0; i < len; ++i)
        {
            int end = num.size() - i * WIDTH;
            int start = max(0, end - WIDTH);
            sscanf(num.substr(start, end - start).c_str(), "%d", &x);
            s.push_back(x);
        }
        standardize();
        return *this;
    }
    BigInteger operator+(const BigInteger &rhs) const
    {
        int size = max(s.size(), rhs.s.size());
        int carry = 0;
        BigInteger ans;
        for (int i = 0; i < size; ++i)
        {
            int sum = carry;
            if (i < s.size())
                sum += s[i];
            if (i < rhs.s.size())
                sum += rhs.s[i];
            carry = sum / BASE;
            ans.s.push_back(sum % BASE);
        }
        if (carry > 0)
        {
            ans.s.push_back(carry);
        }
        return ans;
    }
    BigInteger operator*(const BigInteger &rhs) const
    {
        BigInteger ans;
        for (int i = 0; i < rhs.s.size(); ++i)
        {
            BigInteger lans;
            for (int k = 0; k < i; ++k)
                lans.s.push_back(0);
            int carry = 0;
            for (int j = 0; j < s.size(); ++j)
            {
                int result = rhs.s[i] * s[j] + carry;
                carry = result / BASE;
                lans.s.push_back(result % BASE);
            }
            while (carry > 0)
            {
                lans.s.push_back(carry % BASE);
                carry /= BASE;
            }
            ans = ans + lans;
        }
        return ans;
    }
    BigInteger operator-(const BigInteger &rhs) const
    {
        BigInteger ans;
        int carry = 0;
        for (int i = 0; i < s.size(); ++i)
        {
            int diff = s[i] - carry;
            if (i < rhs.s.size())
                diff -= rhs.s[i];
            carry = 0;
            while (diff < 0)
            {
                ++carry;
                diff += BASE;
            }
            ans.s.push_back(diff);
        }
        ans.standardize();
        return ans;
    }
    BigInteger operator/(int rhs) const
    {
        BigInteger ans;
        vector<int> t;
        long long rmder = 0;
        for (int i = s.size() - 1; i >= 0; --i)
        {
            long long temp = rmder * BASE + s[i];
            long long div = temp / rhs;
            rmder = temp % rhs;
            t.push_back(div);
        }
        for (int i = t.size() - 1; i >= 0; --i)
            ans.s.push_back(t[i]);
        ans.standardize();
        return ans;
    }

    friend ostream &operator<<(ostream &out, const BigInteger &rhs)
    {
        out << rhs.s.back();
        for (int i = rhs.s.size() - 2; i >= 0; --i)
        {
            char buf[5];
            sprintf(buf, "%04d", rhs.s[i]);
            cout << string(buf);
        }
        return out;
    }
    bool operator<(const BigInteger &rhs) const
    {
        if (s.size() != rhs.s.size())
            return s.size() < rhs.s.size();
        for (int i = s.size() - 1; i >= 0; --i)
        {
            if (s[i] != rhs.s[i])
                return s[i] < rhs.s[i];
        }
        return false;
    }
};

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
#endif // LOCAL
    int S, n;
    cin >> S;
    while (S--)
    {
        cin >> n;
        BigInteger ans;
        ans = n;
        BigInteger one;
        one = 1;
        ans = ans * (ans - one) / 2 + ans * (ans - one) * (ans - one - one) * (ans - one - one - one) / 24 + one;
        cout << ans << endl;
    }

    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值