【模拟赛】大难题(卡塔兰数,数位DP,进制转换)

背景

国王之手不喜欢巨人 ,巨人也不喜欢国王之手,但他们有一个共同点:都不喜欢炼金师。
——Deadcells:王座之间

题面

一个阶梯状图形,第一行靠右有 n n n 个色块,第二行靠右有 n − 1 n-1 n1 个色块……一直到第 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} lr1010000

题解

这道题,懒人是很难做出来的。

我们先分析函数 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=1nf(i1)f(ni)

这个递推式和卡塔兰数的递推式一模一样,由于开头的几项也一样,所以 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)(n12n) ,我们是做不出来的。所以记忆不同的一些表达式总是很有必要:
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)(n12n)=(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=17ix

我们把 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=0ai67i11=6i=0(ai7i1ai)=6x/6i=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=lmaxr62SiS2i

由于 S i S_i Si 是七进制下每一位的和,所以最终答案不超过两万!

最后我们可以用数位DP来解决,除了是否抵达上/下界,还要记录 2 i 2i 2i 后续是否进位,不然无法处理 S 2 i S_{2i} S2i

由于需要进制转换,所以用压位高精的话,时间是极小常数的 O ( ∣ r ∣ 2 ) O(|r|^2) O(r2)

CODE

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}

你可能感兴趣的:(数学,动态规划,模拟,C++,算法,数论,动态规划,进制转换)