2020牛客多校第八场E-Enigmatic Partition
https://ac.nowcoder.com/acm/contest/5673/E
题意
定义\(f(n)\)为拆分n为\(a_1,a_2 \dots a_m\)满足相邻两个数差值不能超过1,且最大值和最小值差值为2的拆分个数,T次询问,每次询问\(\sum\limits_{i=l}^r f(i)\)
题解
拆分肯定由三种连续的数构成,枚举\(l\)去初始化\(f(n)\),具体方法是枚举l的数量\(i\),然后枚举\(m=l+1\)的数量\(j\),则\(f[i*l+j*m]=(j-1)/2\)因为可以将两个\(l+1\)拆分为一个\(l\)和一个\(l+2\),一共可以拆分\((j-1)/2\)次,这种情况下,\(l\)的数量一定大于r的数量,所以要反过来再枚举一遍\(l\le r\)的情况。
这部分的复杂度是调和级数的,但是在\(l\)很小的时候运行很慢,可以用完全背包优化\(l<50\)的情况
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct READ {
inline char read() {
#ifdef _WIN32
return getchar();
#endif
static const int IN_LEN = 1 << 18 | 1;
static char buf[IN_LEN], *s, *t;
return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++;
}
template <typename _Tp> inline READ & operator >> (_Tp&x) {
static char c11, boo;
for(c11 = read(),boo = 0; !isdigit(c11); c11 = read()) {
if(c11 == -1) return *this;
boo |= c11 == '-';
}
for(x = 0; isdigit(c11); c11 = read()) x = x * 10 + (c11 ^ '0');
boo && (x = -x);
return *this;
}
} in;
const int N = 1e5 + 50;
ll f[N];
ll dp[N];
int a[N];
int main() {
for (int l = 50; l < N; l++) {
int mid = l + 1;
int r = l + 2;
for (int i = 1; i * l < N; i++) {
for (int j = 3; j * mid + i * l < N; j++) {
f[i*l+j*mid] += (j - 1) / 2;
}
}
for (int i = 0; i * r < N; i++) {
for (int j = 3; i * r + j * mid < N; j++) {
f[i*r+j*mid] += (j - 1) / 2;
}
}
}
for (int l = 1; l < 50; l++) {
memset(dp, 0, sizeof(dp));
a[1] = l; a[2] = l + 1; a[3] = l + 2;
int x = 3 * l + 3;
dp[x] = 1;
for (int i = 1; i <= 3; i++) {
for (int j = x + a[i]; j < N; j++) {
dp[j] += dp[j - a[i]];
}
}
for (int i = 1; i < N; i++) f[i] += dp[i];
}
for (int i = 1; i < N; i++) f[i] += f[i - 1];
int t; in >> t;
int cse = 0;
while (t--) {
int l, r; in >> l >> r;
printf("Case #%d: %lld\n", ++cse, f[r] - f[l - 1]);
}
return 0;
}