新语言
新语言 (lan)
题目描述:
Doris 创造了一种新语言,其中有 \(N\) 个字符,编号为 \(1\) 到 \(N\)。有了字符也就有单词, Doris 的语言中的单词需要满足以下规则:
设任意一个合法的单词为 \({A}\),其中第 \(i\) 个字母记作 \(A_i\),若 \(A_i \times 2 \le N\),则该字母不能作为单词结尾,同时 \(A_{i+1}\) 一定要满足 \(A_i \times 2 \le A_{i+1}\)。
若 \(A_i \times 2 > N\),则 \(A_{i+1}\) 的字符没有限制,且该字符可以作为单词结尾。
现在 Doris 想知道有多少个满足条件的⻓度为 \(M\) 的单词。
输入格式:
第一行两个整数 \(N\),\(M\)。
输出格式:
一行 \(1\) 个整数,表示答案模 \(10^9 + 7\) 的余数。
样例输入 1
2 3
样例输出 1
1
样例输入 2
15 20
样例输出 2
795075710
样例输入 3
1433 2250
样例输出 3
662970601
【数据规模】
- \(10\%\) 的数据,满足 \(1 \le N \le 2\),\(1 \le M \le 3000\)
- \(40\%\) 的数据,满足 \(1 \le N,M \le 3000\)
- \(100\%\) 的数据,满足 \(1 \le N,M \le 1000000\)
解法
\(40\%\) 暴力:\(dp_{i,j}\) 表示当前放的位置是 \(j\),权值为 \(i\),然后 dp 即可拿到 \(40\%\)。
\(100\%\) 做法:注意到不考虑第二个条件,只考虑 \(A_{i} \times 2 \le A_{i+1}\) 那么我们可以得到一个序列:\(\{B\} = 1,2,4,8,16,32,64,... \ \ \because N\le1000000 \therefore |B| \le 19\) 我们称这样的 \(\{B\}\) 为子单词,注意 \(\{B\}\) 的结尾一定是 \(> \frac{N}{2}\)。设 \(dp_{i,j}\) 为末位权值为 \(j\) 长度为 \(i\) 的子单词数量的前缀和(在 \(i\) 这个维度上)。
转移:
前缀和:\(+dp_{i-1,j}\)
前一位取值:\(1 \sim \lfloor \frac{i}{2} \rfloor\) 根据 \(dp\) 前缀和定义,\(\therefore +dp_{\lfloor \frac{i}{2} \rfloor,j-1}\)
\(\therefore dp_{i,j} = dp_{i-1,j}+dp_{\lfloor \frac{i}{2} \rfloor,j-1}\)
提示:先枚举 \(j\) 再枚举 \(i\)。
设 \(dp1_i\) 为长度为 \(i\) 的单词方案总数,枚举当前长度 \(i\) 去除最后一个子单词后单词长度为 \(j\) 。
\(dp1_{i} = dp1_{j}*(dp_{i-j,n}-dp_{i-j,\lfloor \frac{n}{2} \rfloor })\)。
其中 \((dp_{i-j,n}-dp_{i-j,\lfloor \frac{n}{2} \rfloor})\) 表示因为子单词末尾一定要 \(> \frac{n}{2}\) 所以只有可能是 \(\lfloor \frac{n}{2} \rfloor \sim n\)。
根据前缀和即可写出。
至于 \(*dp1_{j} j\) 是剩下的长度,由乘法原理即可得到。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int f[1000010];
signed main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
int i;
f[1]=1;
f[2]=2;
for(i=3;i<=100;i++)
{
if(f[i-2]>n-f[i-1])
{
break;
}
f[i]=f[i-1]+f[i-2];
}
for(int j=i;j>=1;j--)
{
if(n>f[j])
{
n-=f[j];
}
if(n == f[j])
{
cout<<n<<'\n';
return 0;
}
}
return 0;
}

浙公网安备 33010602011771号