题解 CF1105C 【Ayoub and Lost Array】
虽然这题较简单,但我们还是考虑从暴力一点点优化。
做法一
我们考虑枚举每一位是多少,然后判断是否合法。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
namespace Dango
{
const int MAXN=200005,MOD=1000000007;
int n,l,r;
long long ans;
int read()
{
int x=0,f=0;
char ch=getchar();
while(!isdigit(ch)){f|=(ch=='-');ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f?-x:x;
}
void dfs(int a)
{
static long long sum;
if(a>n)
{
if(!(sum%3))
{
ans++;
if(ans>=MOD)ans-=MOD;
}
return;
}
for(int i=l;i<=r;i++)
{
sum+=i;
dfs(a+1);
sum-=i;
}
}
int work()
{
n=read();l=read();r=read();
dfs(1);
printf("%lld",ans);
return 0;
}
}
int main()
{
return Dango::work();
}
时间复杂度:
空间复杂度:
做法二
我们仔细想了想忽然发现,根据题目要求,所有数可以分成三种:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
namespace Dango
{
const int MAXN=200005,MOD=1000000007;
int n,l,r,cnt[3];
long long ans;
int read()
{
int x=0,f=0;
char ch=getchar();
while(!isdigit(ch)){f|=(ch=='-');ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f?-x:x;
}
long long pow_(long long a,long long b)
{
long long res=1;
while(b)
{
if(b&1)res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
void dfs(int a)
{
static long long sum,res=1;
if(a>n)
{
if(!sum)ans=(ans+res)%MOD;
return;
}
for(int i=0;i<3;i++)
{
if(!cnt[i])continue;
sum=(sum+i)%3;
res=res*cnt[i]%MOD;
dfs(a+1);
res=res*pow_(cnt[i],MOD-2)%MOD;
sum=(sum-i+3)%3;
}
}
int work()
{
n=read();l=read();r=read();
cnt[0]=(r+3)/3-(l+2)/3;
cnt[1]=(r+2)/3-(l+1)/3;
cnt[2]=(r+1)/3-l/3;
dfs(1);
printf("%lld",ans);
return 0;
}
}
int main()
{
return Dango::work();
}
时间复杂度:
空间复杂度:
做法三
过了几分钟,我们又发现这题可以 dp。我们设
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
namespace Dango
{
const int MAXN=200005,MOD=1000000007;
int n,l,r,cnt[3];
long long dp[MAXN][3];
long long ans;
int read()
{
int x=0,f=0;
char ch=getchar();
while(!isdigit(ch)){f|=(ch=='-');ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f?-x:x;
}
int work()
{
n=read();l=read();r=read();
cnt[0]=(r+3)/3-(l+2)/3;
cnt[1]=(r+2)/3-(l+1)/3;
cnt[2]=(r+1)/3-l/3;
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
dp[i][0]=(dp[i-1][0]*cnt[0]%MOD+dp[i-1][1]*cnt[2]%MOD+dp[i-1][2]*cnt[1]%MOD)%MOD;
dp[i][1]=(dp[i-1][0]*cnt[1]%MOD+dp[i-1][1]*cnt[0]%MOD+dp[i-1][2]*cnt[2]%MOD)%MOD;
dp[i][2]=(dp[i-1][0]*cnt[2]%MOD+dp[i-1][1]*cnt[1]%MOD+dp[i-1][2]*cnt[0]%MOD)%MOD;
}
printf("%lld",dp[n][0]);
return 0;
}
}
int main()
{
return Dango::work();
}
时间复杂度:
空间复杂度:
注:至此你已经可以AC此题,但我们仍将继续优化。
做法四
我们仔细看了看做法三里的 dp 转移方程,发现
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
namespace Dango
{
const int MAXN=200005,MOD=1000000007;
int n,l,r,cnt[3];
long long dp[2][3];
long long ans;
int read()
{
int x=0,f=0;
char ch=getchar();
while(!isdigit(ch)){f|=(ch=='-');ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f?-x:x;
}
int work()
{
n=read();l=read();r=read();
cnt[0]=(r+3)/3-(l+2)/3;
cnt[1]=(r+2)/3-(l+1)/3;
cnt[2]=(r+1)/3-l/3;
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
dp[i&1][0]=(dp[(i&1)^1][0]*cnt[0]%MOD+dp[(i&1)^1][1]*cnt[2]%MOD+dp[(i&1)^1][2]*cnt[1]%MOD)%MOD;
dp[i&1][1]=(dp[(i&1)^1][0]*cnt[1]%MOD+dp[(i&1)^1][1]*cnt[0]%MOD+dp[(i&1)^1][2]*cnt[2]%MOD)%MOD;
dp[i&1][2]=(dp[(i&1)^1][0]*cnt[2]%MOD+dp[(i&1)^1][1]*cnt[1]%MOD+dp[(i&1)^1][2]*cnt[0]%MOD)%MOD;
}
printf("%lld",dp[n&1][0]);
return 0;
}
}
int main()
{
return Dango::work();
}
时间复杂度:
空间复杂度:
做法五
过了几分钟我们发现,dp 转移方程里
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
namespace Dango
{
const int MOD=1000000007;
struct matrix
{
int x,y;
long long num[3][3];
matrix operator * (const matrix b)const
{
matrix res;
memset(res.num,0,sizeof(res.num));
res.x=x;res.y=b.y;
for(int k=0;k<y;k++)
for(int i=0;i<x;i++)
for(int j=0;j<b.y;j++)
res.num[i][j]=(res.num[i][j]+num[i][k]*b.num[k][j]%MOD)%MOD;
return res;
}
}res,ans;
int n,l,r;
int read()
{
int x=0,f=0;
char ch=getchar();
while(!isdigit(ch)){f|=(ch=='-');ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f?-x:x;
}
matrix pow_(matrix a,long long b)
{
matrix res;
memset(res.num,0,sizeof(res.num));
res.x=res.y=a.x;
for(int i=0;i<res.x;i++)
res.num[i][i]=1;
while(b)
{
if(b&1)res=res*a;
a=a*a;
b>>=1;
}
return res;
}
int work()
{
n=read();l=read();r=read();
res.x=res.y=3;
res.num[0][0]=res.num[1][1]=res.num[2][2]=(r+3)/3-(l+2)/3;
res.num[0][1]=res.num[1][2]=res.num[2][0]=(r+2)/3-(l+1)/3;
res.num[0][2]=res.num[1][0]=res.num[2][1]=(r+1)/3-l/3;
ans.x=1;ans.y=3;
ans.num[0][0]=1;
ans=ans*pow_(res,n);
printf("%lld",ans.num[0][0]);
return 0;
}
}
int main()
{
return Dango::work();
}
时间复杂度:
空间复杂度:
由于本人水平有限,只能把复杂度优化到这里。欢迎各位大佬提出更优秀的做法。