『日常训练』2020WFU个人积分赛第二场

  1. 越狱【组合数学】

    思路:容斥。如果不加限制的话,n个人m种信仰,则共有 m n m^n 种的排列方法,不合法的方案有 m ( m 1 ) ( n 1 ) m*(m-1)^{(n-1)} (第一个位置m种随便放,后面的从m-1种选出一个来即可),结果就是 m n m ( m 1 ) ( n 1 ) m^n-m*(m-1)^{(n-1)}

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5 + 5;
    const int mod = 100003;
    ll ksm(ll a,ll b){
        ll ans = 1;
        while(b){
            if(b&1) ans = ans*a%mod;
            a = a*a%mod;
            b = b >> 1;
        }
        return ans%mod;
    }
    int main(void) {
        ll m,n;
        cin >> m >> n;
        ll ans = ksm(m,n);
        ll ans1 = m*ksm(m-1,n-1);
        cout<<((ans-ans1)%mod+mod)%mod<<endl;
        return 0;
    }
    
    
  2. 转圈游戏【快速幂】

    思路:快速幂

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5 + 5;
    int ksm(int a,int b,int mod){
        int ans = 1;
        while(b){
            if(b&1) ans = ans*a%mod;
            a = a*a%mod;
            b >>= 1;
        }
        return ans%mod;
    }
    int main(void) {
        int n,m,k,x;
        cin >> n >> m >> k >> x;
        int ans = ksm(10,k,n);
        cout<<(x+m*ans%n)%n<<endl;
        return 0;
    }
    
  3. Wonderful Randomized Sum【贪心】
    题意:给你一个数字序列,你可以选择它的一些前缀或后缀(可能是空的)中的每个数字都乘以-1。前缀和后缀可以交叉也可以为空。问能得到的最大序列和是多少。

    思路:应该可以看出,前缀和后缀交叉的情况下,交叉的那部分,相当于没有变化。 那么据题可知:

    设前缀为A,后缀为B,未变化部分的为C。序列数字总和为S(允许A,B,C都为空的情况,此时A=B=C=0,

    因为 A+B+C=S,需求解为 -(A+B)+C 的最大值,所以 —(A+B)+C的最大值 = —(S-C)+C的最大值 = 2C-S。综上 显然S是定值,我们要做的就是让C最大,问题就变成了求最大子段和了。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5 + 5;
    int a[maxn];
    int main(void) {
        int n;
        cin >> n;
        int sum = 0;
        for(int i = 0; i < n; i++){
            cin >> a[i];
            sum += a[i];
        }
        int ans = 0;
        int maxx = 0;
        for(int i = 0; i < n; i++){
            ans += a[i];
            if(ans < 0) ans = 0;
            if(ans>maxx) maxx = ans;
        }
        cout<<2*maxx-sum<<endl;
        return 0;
    }
    
  4. Subsegments【模拟】

    题意:给一个长度为 n 的数组,输出 n-k-1 个数,分别为下标为 i 到 i+k-1 这个区间内存在且只出现一次的数的最大值,不存在则输出 “Nothing”。

    思路:先把前 k 个数只出现一次的数存入 set 集合里,自动从小到大排序,如果不为空则输出最后一个数,否则输出 “Nothing”。 然后从 k+1 开始,每加入一个数就删去上一个区间的第一个数,并判断删去的数剩下的个数是否为 1,若是,则加入可行集合 set 里,否则就看它是否在可行集合里,在的话就删掉。 然后再判断新加入的数的个数是否为 1,是的话加入可行集合里,否则就看它是否在可行集合里,在的话就删掉。 最后再判断该区间的可行集合里是否为空,为空的话就输出 “Nothing”,否则输出最后一个数。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5 + 5;
    int a[maxn];
    map<int,int> mp;
    set<int> s;
    int main(void) {
        int n,k;
        cin >> n >> k;
        for(int i = 1; i <= n; i++) cin >> a[i];
        for(int i = 1; i <= n; i++){
            if(i<=k){
                mp[a[i]]++;
                if(mp[a[i]]==1) s.insert(a[i]);
                else{
                    s.erase(a[i]);
                }
                if(i==k){
                    if(s.empty()) cout<<"Nothing"<<endl;
                    else cout<<*(--s.end())<<endl;
                }
            }
            else{
                mp[a[i]]++;
                mp[a[i-k]]--;
                if(mp[a[i]]==1) s.insert(a[i]);
                else s.erase(a[i]);
                if(mp[a[i-k]]==1)   s.insert(a[i-k]);
                else s.erase(a[i-k]);
                if(s.empty())   cout<<"Nothing"<<endl;
                else    cout<<*(--s.end())<<endl;
            }
        }
        return 0;
    }
    
发布了8 篇原创文章 · 获赞 4 · 访问量 1530

猜你喜欢

转载自blog.csdn.net/wxd1233/article/details/105572612