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;
}

 

转载于:https://www.cnblogs.com/Zhi-71/p/10309284.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值