一道思维题
不仅是和这道题在战斗,我和编译器也进行了一场激烈的角逐
因为编译器出了点小问题...
对于dev或者codeblocks 我的方法是卸载了重新装/重启电脑
但是对于vscode 我的方法是,
对着它掉眼泪,看它能不能可怜可怜我,赶紧恢复到正常状态....
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int long long
const ll N = 2e5 + 1000;
const ll mod = 1e9 + 7;
ll n, dp[N][3],l,r,n0,n1,n2;
int main(){
scanf("%ld%ld%ld", &n,&l,&r);
n0= n1 = n2 = (r - l + 1) / 3;
ll num = (r - l + 1) % 3;
if(num==1){
if(r%3==2)
n2++;
else if(r%3==1)
n1++;
else if(r%3==0)
n0++;
}
else if(num==2){
if(r%3==1){
n0++;
n1++;
}
else if(r%3==2){
n1++;
n2++;
}
else if(r%3==0){
n0++;
n2++;
}
}
dp[1][0] = n0;
dp[1][1] = n1;
dp[1][2] = n2;
for (ll i = 2; i <= n; i++){
dp[i][0] = ( (dp[i - 1][0] * n0)%mod + (dp[i - 1][1] * n2)%mod + (dp[i - 1][2] * n1)%mod )%mod;
dp[i][1] = ( (dp[i - 1][0] * n1)%mod + (dp[i - 1][1] * n0)%mod + (dp[i - 1][2] * n2)%mod )%mod;
dp[i][2] = ( (dp[i - 1][0] * n2)%mod + (dp[i - 1][1] * n1)%mod + (dp[i - 1][2] * n0)%mod )%mod;
}
cout << dp[n][0];
//system("pause");
return 0;
}
这道题,各个版本的解析都说的很简单,我想详细的说一下
题意:
给定一个闭区间,[l,r],要在里面选n个数,保证n个数的和为3的倍数
思路:数论+dp
关于数论部分:
要让n个数的和为3,比如现在有两个数,a和b
有一个简单的前置计划式子,那就是:
((a mod n)+(b mod n) )mod n==(a+b)mod n;
减法和乘法同理;
那么能够满足a+b=3*k,即(a+b)%3==0 也就是
[(a%3)+(b%3) ]%3==0
如果a%3==1,那么b%3为2;
如果a%3==0,那么b%3为0;
如果a%3==2,那么b%3为1
这样才能保证两个数相加能够被3整除
关于dp部分
这里的dp我们用一个二维数组表示,dp[N][3];
对于dp[i][j];
i代表目前已经选择了i个数相加,j表示他们的和的余数目前是0或1或2;
dp数组的值代表选择i个数相加,并且它们的和的余数为0/1/2有多少种;
在for循环里,每一次i++,我们都要选取一个新的数加进我们选择的数组,最后直到我们已经选完了n个数;
那么每次选的时候,比如说我们现在是
dp[i][2] = ( (dp[i - 1][0] * n2)%mod + (dp[i - 1][1] * n1)%mod + (dp[i - 1][2] * n0)%mod )%mod;
对于这个式子,那么就是我们现在已经有i-1个数已经排列好了,并且之前有多少种可能性已经计算过了,现在想再加一个数进去,构成一个长度为i的,和的余数为2的数组;
现在我们想在原本的i-1序列中加一个数,使得和的余数为2,
那么对于dp[i-1][0],要乘上余数为2的数的数量,也就是dp[i-1][0]*n2,这样才可以得到一个余数为2的数,后面的同理
说完了..
原文地址:https://www.cnblogs.com/guaguastandup/p/10353720.html
时间: 2024-10-08 20:55:12