zoukankan      html  css  js  c++  java
  • E. Johnny and Grandmaster

    E. Johnny and Grandmaster

    题意:

    ​ 给定n个数(P={p^{k_i} }),将这n个数划分到两个集合中,使得这两个集合的差值最小,求最小的差值对1e9+7取模。

    思路:

    ​ 将n个数划分到两个集合可以转化为指定这n个数的正负性使得这n个数的和最小。

    (p^{k_i})可以看成是(p)进制表示的第(k_i)位为1,其余位为0的数,例如(p=10,k=3)(p^{k_i}=1000_{10})。按照这种表示方法可以想到,将这n个数按照从大到小的顺序排列,如果第一位取正,那么一定存在一个(i),使得第2到第(i)全部取负时,前(i)位的和位大于等于0,且(i)可以取得尽可能的大。意思就是,如果前(i)位的和大于零,且(i<n),那么(i)还可以扩大。

    ​ 例如,第一位为(10^3),第二位为(10^2),第三位为(10^2)....第(j)位为(10),这种情况,模拟一下就可以明白了。

    [\p^{k_1}:1000_{p}\p^{k_2}:100_{p}\p^{k_3}:100_{p}\p^{k_4}:100_{p}\... ]

    ​ 更加普遍的规律,将这n个数按照从大到小排列后,如果第(i)位取正,那么一定存在一个(j),使得(i+1)(j)全部取负,然后第(i)(j)位的和大于等于0,且(j)可以取得尽可能的大。

    ​ 按照这种思路,很容易得出一个贪心策略,第一位取正,然后后面取负,直至和为0,然后再取一个正,后面的再取负,如此循环直至n个数全部用完,最后的和即为最小的。

    ​ 那么怎么证明呢?数学归纳法。

    ​ 只有一个数的时候,这种策略一定是正确的。

    ​ 假设有n个数的时候这种策略也是正确的。然后当有n+1个数的时候,使用这种策略给n+1个数指定正负,得出前n个数的和为(sum_n),记第n+1个数为(arr_n),如果(sum_n==0),则按照这种策略求得和为(sum_{n+1}=arr_n),如果(sum_{n}>0),则按照这种策略求得和为(sum_{n+1}=sum_n-arr_n)

    ​ 假设存在一种最优策略求得和为(best_{n+1}<sum_{n+1}),记这种更优策略求得前n个数的和为(best_{n})(如果(best_n<0),那么可以把最优策略规定的正负性取反得到等价策略,且(best_nge0)),根具数学归纳法的假设可以知到(sum_nle best_n),即应对前n个数的时候贪心策略不比"最优策略“孬。又因为p进制数的缘故(重点),(best_n>=arr_{n+1}),所以最优策略在面对第n+1个数的时候会做和贪心策略等价的选择,这也就说明最优策略不会得到优于贪心策略的答案,即所谓的最优策略就是贪心策略。

    ​ 到这里我们已经知道为这n个数指定正负性的贪心策略,然后就是实现。

    实现

    ​ 如果我们可以很简单的表示指数级的数,那么这题的实现就会很简单,但问题是我们表示不了,指数级的数不光会爆long long,且运算也耗时。那么该怎么办呢,想到题目中要求答案取模就可以了,那么我们是不是可以根据取模的性质在运算过程中实时的取模呢?

    ​ 答案并非那么简单。因为我们需要根据前面元素的和来决定当前位置的正负性,如果前面元素的和大于零,则当前元素取负,如果前面元素的和为零,则当前元素取正,然后修改这个和。这就产生了一个问题,取模会使得一个数变小,使得本来后面t个元素都负号,现在只能使一两个元素取负然后自身就变为零。

    ​ 那么该怎么办呢?注意到取模是对1e9+7取模,也就是只有我们的和大于这个值的时候才会真正的执行取模,而指数k的取值范围只有1e6那么点,那么我们可以从指数k入手。当算到当前元素的时候,实时计算前缀和是当前元素的多少!!倍!!,然后如果这个倍数需要取模大于1e9的时候,从此往后我们就无需判断是否为零,直接全部取负,因为后面最多1e6元素,而前缀和已经是其中最大值的1e9倍,那还判断什么呢。

    tips

    ​ 与其说前缀和和当前元素的大小关系,不如说前缀和是当前元素的多少倍,从倍数这一概念入手可以更好的理解这题,且更符合进制这一概念。

    垃圾代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN=1e6+10;
    const ll MOD=1e9+7;
    ll qpow(ll p,int q){
        ll res=1;
        while(q){
            if(q&1)res=res*p%MOD;
            q>>=1;
            p=p*p%MOD;
        }
        return res;
    }
    bool tpow(ll p,int q){
        ll res=1;
        while(q){
            if(q&1){
                res=res*p;
                if(res>=MOD)return 1;
            }
            q>>=1;
            p=p*p;
            if(p>=MOD&&q>0)return 1;
        }
        return 0;
    }
    
    int main() {
        int T;
        scanf("%d", &T);
        while (T--) {
            bool flag=0;
            int n,p;
            scanf("%d%d",&n,&p);
            vector<int>k;
            int temp;
            for(int i=0;i<n;i++){
                scanf("%d",&temp);
                k.push_back(temp);
            }
            sort(k.begin(),k.end());
            ll res=1;
            int prek=k.back();
            k.pop_back();
            int nowk;
            while(!k.empty()){
                nowk=k.back();
                k.pop_back();
                if(!flag) {
                    if((tpow(p, prek - nowk)&&res!=0)||res*qpow(p,prek-nowk)>=MOD){
                        flag=1;
                        res=res*qpow(p,prek-nowk)%MOD;
                        res = (res - 1 + MOD) % MOD;
                    }
                    else {
                        res=res*qpow(p,prek-nowk);
                        if (res>0) {
                            res = res-1;
                        } else {
                            res = 1;
                        }
                    }
                }
                else{
                    res = res * qpow(p, prek - nowk) % MOD;
                    res = (res - 1 + MOD) % MOD;
                }
                prek = nowk;
            }
            res=res*qpow(p,prek)%MOD;
            printf("%lld
    ",res);
        }
        return 0;
    }
    

    总结

    ​ 理解能力还是不足,运用不够灵活,差点学死了....

  • 相关阅读:
    添加右键菜单
    闭包和迭代器
    函数的进阶
    函数入门
    文件操作
    深浅拷贝
    小数据池和再谈编码
    字典
    list tuple
    int bool str
  • 原文地址:https://www.cnblogs.com/dialectics/p/13052739.html
Copyright © 2011-2022 走看看