P10259 [COCI 2023/2024 #5] Piratski kod
题链
题意
首先,题目写的很抽象,模拟赛时读了半个小时才读懂
题意概括一下就是枚举长度为k的所有01串
然后对01串进行划分,每遇到两个1就进行一次划分
然后把每段提取出来单独处理
如果把提出来的01串计为\(s[1...r]\)
则改01串的价值为\(\sum_{i=1}^{r-1}s[i]*f[i+1]\)
其中f数组为斐波那契数列,\(f[1]=1\),\(f[2]=1\),然后递推即可
最后分别输出长度为\(k\)的01串价值和,不同k之间独立,\({k=1,2...n}\)
Solution
我们现在处理长度为k的01串,我们已经知道了前一个的答案
考虑这个k无非是在长度为k-1的每个串后面加上0/1构成的
那么,我们考虑,我们如果加上一个0,贡献是不会变的,是一个ans[k-1]
如果我们加上一个1,会有什么变化呢?
会使原本最后一段没有贡献,且末尾为1的产生贡献
那么现在我们就可以转移了
我们先无脑处理出来几个数组
\({fa_{i,0/1}},dp_{i,0/1},fx_{i}\)
\({fa_{i,0/1}}\)表示长度为i,结尾是0/1,中间没有连续1的方案数
\({dp_{i,0/1}}\)表示长度为i,结尾是0/1,中间没有连续1的贡献,注意:包括最后一个1
\({fx_{i}}\)表示长度为i,以1结尾且结尾开始到前面第一个0的长度为偶数的数量
转移式子很简单
\(fa_{i,0}=fa_{i-1,0}+fa_{i-1,1},fa_{i,1}=fa{i-1,0}\)
\({dp_{i,0}=dp_{i-1,0}+dp_{i-1,1}},dp_{i,1}=dp_{i-1,0}+fa_{i-1,0}*f_{i+1}\)
\(fx_{i}=\sum_{j=2}^{j+=2,j<i}2^{i-j-1}+!(i\)%\(2)\)
然后答案就很简单了
\(ans_i=\sum_{j=1}^{i-2}dp_{j,1}*fx_{i-j-1}+dp_{i-1,1}+ans_{i-1}*2\)
\({\sum_{j=1}^{i-2}dp_{j,1}*fx_{i-j-1}+dp_{i-1,1}}\)这个是枚举最后一段区间的长度为{j}时的贡献
最后输出即可
也是非常简单
/*ILVWYT*/
#include<bits/stdc++.h>
#define in :
#define int __int128
#define IfFast
using namespace std;
namespace fast {
#ifdef IfFast
void read(int &a) {
int s = 0, f = 1;
char ch = getchar();
while (!(ch <= '9' && ch >= '0')) {
if (ch == '-') {
f = -1;
}
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
s = s * 10 + ch - '0';
ch = getchar();
}
a = s * f;
return void();
}
void read(char &a) {
char x = getchar();
while (x == ' ' || x == '\r' || x == '\n' || x == '\t') {
x = getchar();
}
a = x;
return void();
}
void print(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x < 10) {
putchar(x + '0');
} else {
print(x / 10);
putchar(x % 10 + '0');
}
return void();
}
#endif
template<typename T>
void read(T &a) {
cin >> a;
return void();
}
template<typename T, typename... Args>
void read(T &a, Args &... args) {
read(a);
read(args...);
return void();
}
inline int read() {
int a;
read(a);
return a;
}
template<typename T>
void print(T a) {
cout << a;
return void();
}
template<typename T, typename... Args>
void print(T a, Args... args) {
print(a);
print(args...);
return void();
}
}
using fast::read;
using fast::print;
using us = unsigned;
constexpr int mod = 1e9 + 7;
constexpr int N = 10000;
int n;
int f[N + 20];
int ans[N + 10];
int dp[N + 10][2]; //长度为n的,以1/0结尾的,没有连续1的贡献
int fa[N + 10][2];
inline int quick(int x,int y) {
int ans = 1;
while (y) {
if (y & 1) {
ans *= x;
ans %= mod;
}
x *= x;
x %= mod;
y >>= 1;
}
return ans;
}
int fx[N + 10]; //长度为n,结尾1的个数还是偶数的方案数
signed main() {
#ifndef IfFast
ios::sync_with_stdio(false);
#endif
read(n);
f[1] = 1;
f[2] = 1;
for (us i = 3; i <= n + 10; i++) {
f[i] = f[i - 1] + f[i - 2];
f[i] %= mod;
}
dp[1][1] = 1;
fa[1][1] = 1;
fa[1][0] = 1;
for (us i = 2; i <= n; i++) {
fa[i][1] = fa[i - 1][0];
fa[i][0] = fa[i - 1][0] + fa[i - 1][1];
fa[i][0] %= mod;
fa[i][1] %= mod;
dp[i][0] = dp[i - 1][0] + dp[i - 1][1];
dp[i][1] = dp[i - 1][0] + fa[i - 1][0] * f[i + 1] % mod;
dp[i][0] %= mod;
dp[i][1] %= mod;
if (i % 2) {
for (us j = 2; j < i; j += 2) {
fx[i] += quick(2, i - 1 - j) % mod;
fx[i] %= mod;
}
} else {
for (us j = 2; j < i; j += 2) {
fx[i] += quick(2, i - 1 - j) % mod;
fx[i] %= mod;
}
fx[i]++;
fx[i] %= mod;
}
}
// print(fx[3], ' ', fx[4], '\n');
ans[2] = 1;
ans[3] = 4;
fx[0] = 1;
for (us i = 3; i <= n; i++) {
ans[i] = ans[i - 1] * 2 % mod;
for (us j = 1; j < i - 2; j++) {
const int ws = i - j - 1;
ans[i] += dp[j][1] * fx[ws] % mod;
ans[i] %= mod;
}
ans[i] += dp[i - 1][1];
ans[i] %= mod;
}
for (us i = 1; i <= n; i++) {
print(ans[i], ' ');
}
return 0;
}
还有开int128,不然会wa(本地跑很对)

抽象的题意
浙公网安备 33010602011771号