• Atcoder Beginner Contest 200 E


    传送门

    当想了半个多小时三维坐标系的菜比看到了组合数学题解


    题意

    (1leq i, j, kleq N)(N^3) 个三元组 ((i, j, k)) 进行排序,排序时以总和为第一关键字,第一维为第二关键字,第二维为第三关键字,从小到大排。

    询问第 (K) 个三元组


    分析

    如果用三维坐标系描述,不难想到,三元组分布在长宽高均为 (N) 的正方体上;将其按 (x+y+z=C) 进行切片,就能得到第一关键字;同理分析得到第二第三。

    如果具有极其恐怖的空间想象能力,可以试试这个方法。反正我是不行

    考虑 (i+j+k=S) 的方案数:

    等效于 (S) 个小球塞入 (3) 个盒子,每个盒子非空且容积为 (n) ,问方案数。

    不考虑 (n) 的限制,则方案数用隔板法得 (dbinom{S-1}{2})

    现考虑有一个数超过 (n) 的限制,我们对它容斥:先分配给这个数 (n) ,再用隔板法分配得 (dbinom{S-n-1}{2});超过 (n) 的那个数有 (dbinom{3}{1}) 种选法。

    同理容斥两个数、三个数超过 (n) 限制的情况,得到最后方案数为:

    (displaystyle sum_{i=0}^3(-1)^idbinom{3}{i}cdot dbinom{S-icdot n-1}{2})

    由于组合数下方的数字都较小,直接暴力展开可求,复杂度 (O(1))

    因为层数最高为 (3n) ,直接暴力枚举过去,找到在第几层即可,复杂度为 (O(n))


    求出第一关键字后,我们得到了 (i+j+k=S) 的答案,然后考虑求 (i)

    我们直接从 (1)(n) 枚举 (i) ,则此时 (j+k=S-i) 的方案数:

    等效于 (S-i) 个小球塞入 (2) 个盒子,每个盒子非空且容积为 (n) ,问方案数。

    同上得到为: (displaystyle sum_{j=0}^2(-1)^jdbinom{2}{j}cdot dbinom{S-i-jcdot n-1}{1})

    同理直接展开,然后暴力枚举,复杂度为 (O(n)) 即可确定 (i)


    确定 (i)(S) 后,我们直到 (j+k=S-i) ,则 (j_{min}=max(S-i-n, 1))

    当我们筛除上面的方案数,确定 ((i, j, k))(j+k=S-i) 的第 (K) 个后,则 (j_K=j_{min}+K-1)

    然后用 (k=S-i-j_K) 求出 (k) 输出即可。


    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN=3e6+10;
    ll n, k, sum[MAXN];
    inline ll calc3(ll k){
        ll res=(k-1)*(k-2)/2;
        k-=n;
        if(k<=0) return res;
        res-=(k-1)*(k-2)/2*3;
        k-=n;
        if(k<=0) return res;
        res+=(k-1)*(k-2)/2*3;
        k-=n;
        if(k<=0) return res;
        return res-(k-1)*(k-2)/2;
    }
    inline ll calc2(ll k){
        ll res=k-1;
        k-=n;
        if(k<=0) return res;
        res-=(k-1)*2;
        k-=n;
        if(k<=0) return res;
        return res+(k-1);
    }
    inline void work(){
        ll flr=3;
        while(sum[flr]<k)
            k-=sum[flr++];
        for(int i=1;i<=n;++i){
            ll res=calc2(flr-i);
            if(res<k){
                k-=res;
                continue;
            }
            ll x=max(flr-i-n, 1ll)+(k-1);
            cout<<i<<" "<<x<<" "<<flr-i-x;
            break;
        }
    }
    inline void pre(){
        cin>>n>>k;
        for(int i=1;i<=3*n;++i) sum[i]=calc3(i);
    }
    int main(){
        ios::sync_with_stdio(0);
        cin.tie(0); cout.tie(0);
        pre();
        work();
        cout.flush();
        return 0;
    }
    
  • 相关阅读:
    结婚也可以2.0!
    最近读书清单
    Win7旗舰版中的IIS配置asp.net的运行环境
    Google Chrome浏览器
    MySQL到NoSQL:数据的重思和查询方式的转换
    sqlserver游标概念与实例全面解说
    高效管理ASP.NET的JavaScript库
    如何提取网页中的flash
    如何手动启动tomcat
    tomcat单词缩写
  • 原文地址:https://www.cnblogs.com/JustinRochester/p/14826783.html
Copyright © 2020-2023  润新知