zoukankan      html  css  js  c++  java
  • 「模拟赛20180307」三元组 exclaim 枚举+树状数组

    题目描述

    给定 \(n,k\) ,求有多少个三元组 \((a,b,c)\) 满足 \(1≤a≤b≤c≤n\)\(a + b^2 ≡ c^3\ (mod\ k)\)

    输入

    多组数据,第一行数据组数\(T\)
    每组数据两个整数,\(n\)\(k\)

    输出

    \(T\)行,每行一个整数,表示满足条件的三元组的个数。

    样例

    样例输入

    1
    10 7
    

    样例输出

    27
    //为什么老被和谐啊
    

    数据范围

    \(1≤n,k≤10^5\)
    \(T≤400\)
    时间限制\(4s\)

    题解

    与其他学校互测,然后做题感觉很不友好……
    这道题数据很有特点(哪里很有特点了),\(10^5\)显然是一个象征性的数字,它意味着\(O(n\ log\ n)\)是可以过的(这么大的\(T\)被无视了啊)。
    那么很自然的想到,这个式子并没有什么规律(我也很无奈啊),我们可以考虑枚举\(a,b,c\)中的\(1\)个。
    但是我们选择哪一个比较好呢?容易想到,应该是\(c\),它的次数最高,不易计算。

    接下来考虑一个简化的问题,如果不取余\(k\),该怎么办?
    对于一个数\(b\),由于\(1≤a≤b\),显然\(c^3\)只有在\([b^2+1,b^2+b]\)范围内才有解,而且是唯一解。
    所以每一个\(b\)可以为在\([b^2+1,b^2+b]\)\(c^3\)提供一个解,这不就是区间增加一个值吗?树状数组即可做到。

    再考虑取余\(k\)时,发现情况如出一辙,一样的做就可以了。唯一一个问题就是,\([b^2+1,b^2+b]\)可能长度超过了\(k\)
    这时能发现长度超过\(k\)后完全覆盖了所有区域,任何一个\(c\)都可以使用这个\(b\),我们只需要一个计数器\(count\),每次增加\(\left \lfloor\frac{b}{k}\right \rfloor\)

    现在,这道题的解法就呼之欲出了。我们从小到大枚举\(c\),先在树状数组\((tree)\)\([c^2+1,c^2+c]\)的区间加上\(1\),并更新\(count\),答案就等于\(tree[c^3\%k]+count\)。时间复杂度为\(O(Tn\ log\ n)\)(再说一次请无视\(T\)的大小)

    \(Code:\)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define N 100005
    #define ll long long
    int T, n, mod;
    ll ans, now, t[N];
    void update(int x, int v)
    {
        for (int i = x; i <= mod; i += i & -i)
            t[i] += v;
    }
    ll getsum(int x)
    {
        ll ans = 0;
        for (int i = x; i; i -= i & -i)
            ans += t[i];
        return ans;
    }
    int main()
    {
        freopen("exclaim.in", "r", stdin);
        freopen("exclaim.out", "w", stdout);
        scanf("%d", &T);
        for (int cas = 1; cas <= T; cas++)
        {
            scanf("%d%d", &n, &mod);
            ans = now = 0;
            memset(t, 0, sizeof(t));
            for (int i = 1; i <= n; i++)
            {
                int l =(1ll * i * i + 1)% mod + 1, r =(1ll * i * i + i)% mod + 1;
                if (l <= r)
                    update(l, 1), update(r + 1, -1);
                else
                    update(1, 1), update(r + 1, -1), update(l, 1);
                int c = 1ll * i * i % mod * i % mod;
                now +=(i - 1)/ mod;
                ans += getsum(c + 1) + now;
            }
            printf("Case %d: ", cas);
            cout << ans;
            putchar(10);
        }
    }
    

    最后的吐槽:\(exclaim\)并不是三元组的意思,是惊叫的意思……至于为什么,我也不知道……

  • 相关阅读:
    [TJOI2018]教科书般的亵渎
    luogu P4781 【模板】拉格朗日插值
    [SDOI2010]捉迷藏
    [CQOI2016]K远点对
    BZOJ4066 简单题
    [国家集训队]JZPFAR
    Understanding User and Kernel Mode
    Linux下如何查看系统启动时间和运行时间以及安装时间
    CentOS Linux搭建独立SVN Server全套流程(修改svn仓库地址、服务启动等)
    linux下获取占用CPU资源最多的10个进程,可以使用如下命令组合:
  • 原文地址:https://www.cnblogs.com/ModestStarlight/p/8528555.html
Copyright © 2011-2022 走看看