背景
国王之手不喜欢巨人 ,巨人也不喜欢国王之手,但他们有一个共同点:都不喜欢炼金师。
——Deadcells:王座之间
题面
一个阶梯状图形,第一行靠右有 n n n 个色块,第二行靠右有 n − 1 n-1 n−1 个色块……一直到第 n n n 行。令 f ( n ) f(n) f(n) 等于用最少的矩形不重叠地刚好覆盖所有色块的方案数。
定义 V ( x ) V(x) V(x) 为 x x x 的质因数分解中 7 的幂。
求 max i = l r V ( ( i + 1 ) f ( i ) ) \max_{i=l}^r V((i+1)f(i)) maxi=lrV((i+1)f(i)) 。
l ≤ r ≤ 1 0 10000 l\leq r\leq 10^{10000} l≤r≤1010000 。
题解
这道题,懒人是很难做出来的。
我们先分析函数
f
(
n
)
f(n)
f(n) ,容易发现
f
(
n
)
f(n)
f(n) 的递推式:
f
(
n
)
=
∑
i
=
1
n
f
(
i
−
1
)
f
(
n
−
i
)
f(n)=\sum_{i=1}^{n} f(i-1)f(n-i)
f(n)=i=1∑nf(i−1)f(n−i)
这个递推式和卡塔兰数的递推式一模一样,由于开头的几项也一样,所以 f ( n ) f(n) f(n) 就是卡塔兰数。
如果我们列出卡塔兰数的这个表达式
f
(
n
)
=
(
2
n
n
)
−
(
2
n
n
−
1
)
f(n)={2n\choose n}-{2n\choose n-1}
f(n)=(n2n)−(n−12n) ,我们是做不出来的。所以记忆不同的一些表达式总是很有必要:
f
(
n
)
=
(
2
n
n
)
−
(
2
n
n
−
1
)
=
(
2
n
n
)
−
n
n
+
1
⋅
(
2
n
n
)
=
(
2
n
n
)
n
+
1
f(n)={2n\choose n}-{2n\choose n-1}={2n\choose n}-\frac{n}{n+1}\cdot {2n\choose n} =\frac{2n\choose n}{n+1}
f(n)=(n2n)−(n−12n)=(n2n)−n+1n⋅(n2n)=n+1(n2n)
代进去,刚好
max
i
=
l
r
V
(
(
i
+
1
)
f
(
i
)
)
=
max
i
=
l
r
V
(
(
2
i
i
)
)
=
max
i
=
l
r
V
(
(
2
i
)
!
)
−
2
V
(
i
!
)
\max_{i=l}^r V((i+1)f(i))=\\~\\ \max_{i=l}^r V\left({2i\choose i}\right)= \\\max_{i=l}^r V((2i)!)-2V(i!)
i=lmaxrV((i+1)f(i))= i=lmaxrV((i2i))=i=lmaxrV((2i)!)−2V(i!)
接下来冷静分析
V
(
x
!
)
V(x!)
V(x!):
V
(
x
!
)
=
∑
i
=
1
⌊
x
7
i
⌋
V(x!)=\sum_{i=1} \left\lfloor \frac{x}{7^i} \right\rfloor
V(x!)=i=1∑⌊7ix⌋
我们把
x
x
x 表示成
7
7
7 进制数
∑
i
=
0
a
i
7
i
\sum_{i=0} a_i7^i
∑i=0ai7i 就好办了
V
(
x
!
)
=
∑
i
=
0
a
i
7
i
−
1
−
1
6
=
∑
i
=
0
(
a
i
7
i
−
1
−
a
i
)
6
=
⌊
x
/
6
⌋
−
∑
i
=
0
a
i
6
V(x!)=\sum_{i=0} a_i\frac{7^{i-1}-1}{6}=\frac{\sum_{i=0} (a_i7^{i-1}-a_i)}{6}=\frac{\lfloor x/6\rfloor-\sum_{i=0}a_i}{6}
V(x!)=i=0∑ai67i−1−1=6∑i=0(ai7i−1−ai)=6⌊x/6⌋−∑i=0ai
我们再令
S
x
=
∑
i
=
0
a
i
S_x=\sum_{i=0}a_i
Sx=∑i=0ai ,答案式子就很直观了
max
i
=
l
r
V
(
(
2
i
)
!
)
−
2
V
(
i
!
)
=
max
i
=
l
r
2
S
i
−
S
2
i
6
\max_{i=l}^r V((2i)!)-2V(i!)=\max_{i=l}^r \frac{2S_i-S_{2i}}{6}
i=lmaxrV((2i)!)−2V(i!)=i=lmaxr62Si−S2i
由于 S i S_i Si 是七进制下每一位的和,所以最终答案不超过两万!
最后我们可以用数位DP来解决,除了是否抵达上/下界,还要记录 2 i 2i 2i 后续是否进位,不然无法处理 S 2 i S_{2i} S2i 。
由于需要进制转换,所以用压位高精的话,时间是极小常数的 O ( ∣ r ∣ 2 ) O(|r|^2) O(∣r∣2) 。
CODE
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 10005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
static const int maxn = 1000000;
static char b[maxn];
static int pos = 0,len = 0;
if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
if(pos == len) return -1;
return b[pos ++];
}
//#define getchar() xchar()
LL read() {
LL f = 1,x = 0;int s = getchar();
while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x<0) putchar('-'),x = -x;
return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}
int n,m,s,o,k;
char ll[MAXN],rr[MAXN];
const int B = 100000000;
struct it{
vector<int> s;
int n;
it(){s.resize(2); s[n=1] = 0;}
it(int x){s.resize(2); s[n=1] = x;}
int MD(int x) {
int as = 0;
for(int i = n;i > 0;i --) {
as = (as*B%x + s[i]) % x;
}return as;
}
bool emp() {return n == 1 && s[1] == 0;}
};
it operator + (it a,it b) {
int m = 0;
for(int i = 1;i <= a.n || i <= b.n || m;i ++) {
if(i >= a.s.size()) a.s.push_back(0);
if(i > a.n) a.s[++ a.n] = 0;
if(i >= b.s.size()) b.s.push_back(0);
if(i > b.n) b.s[++ b.n] = 0;
a.s[i] = a.s[i] + b.s[i] + m;
if(a.s[i] > B) a.s[i] -= B,m = 1;
}
return a;
}
it operator * (it a,it b) {
it c; if(a.n > b.n) swap(a,b);
for(int i = 1;i <= a.n;i ++) {
int m = 0;
for(int j = 1;j <= b.n || m;j ++) {
if(j >= b.s.size()) b.s.push_back(0);
if(j > b.n) b.s[++ b.n] = 0;
if(i+j-1 >= c.s.size()) c.s.push_back(0);
if(i+j-1 > c.n) c.s[++ c.n] = 0;
LL nm = (c.s[i+j-1] + a.s[i]*1ll*b.s[j] + m);
m = nm/B; c.s[i+j-1] = nm%B;
}
}return c;
}
it operator / (it a,int b) {
LL m = 0;
for(int i = a.n;i > 0;i --) {
m = m*B + a.s[i];
a.s[i] = m / b; m %= b;
} while(a.n > 1 && a.s[a.n] == 0) a.n --;
return a;
}
int a[MAXN<<2],b[MAXN<<2];
int dp[MAXN<<2][2][2][2];
int main() {
freopen("dingdingcar.in","r",stdin);
freopen("dingdingcar.out","w",stdout);
it L,R;
scanf("%s",ll + 1);
n = strlen(ll + 1);
scanf("%s",rr + 1);
m = strlen(rr + 1);
for(int i = 1;i <= n;i ++) L = L * it(10) + it(ll[i]-'0');
for(int i = 1;i <= m;i ++) R = R * it(10) + it(rr[i]-'0');
n = 0; m = 0;
while(!L.emp()) a[++ n] = L.MD(7),L = L/7;
while(!R.emp()) b[++ m] = R.MD(7),R = R/7;
int le = max(n,m);
for(int s = 0;s < 2;s ++) {
for(int o = 0;o < 2;o ++) {
for(int k = 0;k < 2;k ++) {
dp[le+2][s][o][k] = -0x3f3f3f3f; // 44 65
}
}
}
dp[le+2][0][0][0] = 0;
for(int i = le+1;i > 0;i --) {
for(int s = 0;s < 2;s ++) {
for(int o = 0;o < 2;o ++) {
for(int k = 0;k < 2;k ++) {
dp[i][s][o][k] = -0x3f3f3f3f;
}
}
}
for(int s = 0;s < 2;s ++) {
for(int o = 0;o < 2;o ++) {
int nl = (s ? 0:a[i]),nr = (o ? 6:b[i]);
for(int x = nl;x <= nr;x ++) {
bool k1 = ((x<<1)>=7 ? 1:0),k2 = ((x<<1|1)>=7 ? 1:0);
bool A = (s || x > a[i]),B = (o || x < b[i]);
dp[i][A][B][0] = max(dp[i][A][B][0],dp[i+1][s][o][k1] + x*2 - (x*2%7));
dp[i][A][B][1] = max(dp[i][A][B][1],dp[i+1][s][o][k2] + x*2 - ((x<<1|1)%7));
}
}
}
}
int ans = 0;
for(int s = 0;s < 2;s ++) {
for(int o = 0;o < 2;o ++) {
ans = max(ans,dp[1][s][o][0]);
}
}
ans /= 6;
AIput(ans,'\n');
return 0;
}