CF1775F 题解
前言
这道题我的做法是枚举和动态规划。
这道题对思维能力及模型转化能力有一定要求,且要用到多次动态规划(虽然每次都比较好想),所以是道不错的题。只是可能会有些难想。
感觉其他的大部分题解讲解的真的是清晰得不太明显(因为我很菜),然后我打算针对我思考时所想的时间较长的地方进行较详细的讲解,其他地方略写。
题意
这翻译似乎不那么简洁。
题目大意:在一个无限大的网格图中,告诉你一个大小
- 让你输出任意一种方案。
- 让你求方案数。
至于格式见题面的输入输出。
思路
不难发现,所有的格子一定是四联通的,否则周长一定不优于这个。
不难发现,一个凸的图形一定比凹的优。 也就是说,在确定面积的时候周长一定是凸的小。
不难发现,通过
不难发现,我们可以先算去掉一个角的方案数,然后拿一个角的方案数合并四次变成四个角。而对于一个角,既然是凸的,那必定是个阶梯型,不然就凹进去了(既然是阶梯,那么每行去掉的数是具有单调性的,所以发现是个分拆数)。那么可以用这题里面的动态规划方法算出动态规划数组,求个和,及每个角,再用背包的思想把数组合并进一个背包数组,合并四次。
不难发现,做的第一次动态规划,即分拆数的状态为
不难发现,这道题的思路已经结束了。
代码
#include <bits/stdc++.h>
#define L(i, a, b) for(int i = a; i <= b; i++)
#define R(i, a, b) for(int i = a; i >= b; i--)
using namespace std;
const int N = 1000;
int n, m, g[N + 10][N + 10], s[N + 10], f[N + 10];
void Init(){
g[0][0] = 1;
L(i, 1, N) L(j, 1, i)
g[i][j] = (g[i - 1][j - 1] + g[i - j][j]) % m;
L(i, 0, N) L(j, 0, i) s[i] = (s[i] + g[i][j]) % m;
f[0] = 1;
L(i, 1, 4) R(j, N, 0) L(k, 1, j)
f[j] = (f[j] + 1ll * f[j - k] * s[k] % m) % m;
}
void Solve1(){
scanf("%d", &n);
int a = sqrt(n), b = (n - 1) / a + 1, cnt = 0;
printf("%d %d\n", a, b);
L(i, 1, a){
L(i, 1, b)
putchar(++cnt <= n? '#' : '.');
putchar('\n');
}
}
void Solve2(){
scanf("%d", &n);
int a = sqrt(n), b = (n - 1) / a + 1, c = a + b, ans = 0;
L(i, 1, c - 1)
if(i * (c - i) >= n)
ans = (ans + f[i * (c - i) - n]) % m;
c *= 2, printf("%d %d\n", c, ans);
}
int main(){
int T, u;
scanf("%d%d", &T, &u);
if(u == 1) while(T--) Solve1();
else{
scanf("%d", &m), Init();
while(T--) Solve2();
}
return 0;
}