• C. Ayoub and Lost Array


    题目来源:http://codeforces.com/contest/1105/problem/C

    题意:给出一个数组的长度以及数组中元素的取值范围,问:有多少种方案使得数组的元素的总和为三的倍数。

    解题思路:我们可以把可取的数字分为三类:对三取模为零,对三取模为一,对三取模为二。先分别统计这三类取值的个数,先假设个数分别为a,b,c,然后定义某个状态dp【i】【j】为数组长度为i的总和对三取模的值的方案数,那么可以得到dp【i+1】【0】=dp【i】【0】*a+dp【i】【1】*c+dp【i】【j】*b,dp【i+1】【1】=dp【i】【0】*b+dp【i】【1】*a+dp【i】【2】*c,dp【i】【2】=dp【i】【0】*c+dp【i】【1】*b+dp【i】【2】*a。最后答案就是:dp【n】【0】。

     #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #include<stack>
    #include<cstdio>
    #include<map>
    #include<set>
    #include<string>
    #include<queue>
    using namespace std;
    #define inf 0x3f3f3f3f
    #define mod 1000000007LL
    typedef long long ll;
    inline ll gcd(ll i,ll j){
        return j==0?i:gcd(j,i%j);
    }
    inline ll lcm(ll i,ll j){
        return i/gcd(i,j)*j;
    }
    int f(int a,int b){
        return a-b>0?a-b:b-a;
    }
    const int maxn=2e5+10;
    ll dp[maxn][5];
    int main(){
        int n,l,r;
        scanf("%d%d%d",&n,&l,&r);
        ll a,b,c,k,k1;
        a=b=c=(r-l+1)/3;
        for(int i=0;i<(r-l+1)%3;i++){
            if((l+i)%3==0)
            a++;
            else if((l+i)%3==1)
            b++;
            else
            c++;
        }
        dp[1][0]=a;
        dp[1][1]=b;
        dp[1][2]=c;
        for(int i=2;i<=n;i++){
            dp[i][0]=(dp[i-1][0]*a%mod+dp[i-1][1]*c%mod+dp[i-1][2]*b%mod)%mod;
            dp[i][1]=(dp[i-1][0]*b%mod+dp[i-1][1]*a%mod+dp[i-1][2]*c%mod)%mod;
            dp[i][2]=(dp[i-1][0]*c%mod+dp[i-1][1]*b%mod+dp[i-1][2]*a%mod)%mod;
        } 
        cout<<dp[n][0];
        return 0;
    }

    既然得到了状态转移方程,那么也很容易用矩阵来得到答案了

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #include<stack>
    #include<cstdio>
    #include<map>
    #include<set>
    #include<string>
    #include<queue>
    using namespace std;
    #define inf 0x3f3f3f3f
    #define mod 1000000007LL
    typedef long long ll;
    inline ll gcd(ll i,ll j){
        return j==0?i:gcd(j,i%j);
    }
    inline ll lcm(ll i,ll j){
        return i/gcd(i,j)*j;
    }
    struct st{
        ll mp[3][3];
        void init(){
            for(int i=0;i<3;i++){
                for(int j=0;j<3;j++){
                    mp[i][j]=(i==j);
                }
            }
        }
    }tem1,tem2;
    st mul(st a,st b){
        st c;
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++){
                c.mp[i][j]=0;
            }
        }
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++){
                for(int k=0;k<3;k++){
                    c.mp[i][j]=(c.mp[i][j]+a.mp[i][k]*b.mp[k][j]%mod)%mod;
                }
            }
        }
        return c;
    }
    st q_pow(st a,int n){
        st b;
        b.init();
        while(n!=0){
            if(n%2==1){
                b=mul(a,b);
            }
            a=mul(a,a);
            n/=2;
        }
        return b;
    }
    int main(){
        int n,l,r;
        scanf("%d%d%d",&n,&l,&r);
        ll a,b,c,k,k1;
        a=b=c=(r-l+1)/3;
        for(int i=0;i<(r-l+1)%3;i++){
            if((l+i)%3==0)
            a++;
            else if((l+i)%3==1)
            b++;
            else
            c++;
        }
        tem1.mp[0][0]=a;
        tem1.mp[0][1]=b;
        tem1.mp[0][2]=c;
        tem2.mp[0][0]=a;tem2.mp[0][1]=b;tem2.mp[0][2]=c;
        tem2.mp[1][0]=c;tem2.mp[1][1]=a;tem2.mp[1][2]=b;
        tem2.mp[2][0]=b;tem2.mp[2][1]=c;tem2.mp[2][2]=a;
        cout<<mul(tem1,q_pow(tem2,n-1)).mp[0][0];
        return 0;
    }
  • 相关阅读:
    android handle详解
    android面试详解
    linux网络编程-一个简单的线程池(41)
    linux网络编程-posix条件变量(40)
    如何写一个简单的分页
    jQuery 3 有哪些新东西
    浅析正则表达式模式匹配的 String 方法
    jQuery源码浅析2–奇技淫巧
    前端文本截断
    你会用setTimeout吗
  • 原文地址:https://www.cnblogs.com/Zhi-71/p/10309284.html
Copyright © 2020-2023  润新知