A. 小Ho的强迫症 数论 GCD
题意:
一个人在无限长的路上走格子,步长为(D),脚长为(F),每个格子的长度为(L),如图所示:
问能否一直走下去,永远踩不到线。
分析:
先不考虑脚的长度,观察一下在所有格子中落点的情况:
容易知道在每个格子中的落点到格子起点的距离都是(g),(2g),(3g cdots),其中(g=gcd(D, ; L))
即落点以(g)等间距地分布在格子上,所以如果脚长(F>g),一定会踩到格子的边线。
#include <cstdio>
int gcd(int a, int b) { return !b ? a : gcd(b, a%b); }
int main()
{
int T; scanf("%d", &T);
while(T--) {
int L, F, D; scanf("%d%d%d", &L, &F, &D);
int g = gcd(L, D);
if(F <= g) puts("YES");
else puts("NO");
}
return 0;
}
B. 拆字游戏 BFS
题意:
有一个(01)矩阵,求出所有四方向的连通块并输出。
分析:
找连通块可以用BFS,主要说一下这题的坑点:
- 代表点是连通块的最左且最上的点,而不是所在矩阵的左上角。
- 输出连通块所在矩阵时,注意不要输出其他连通块的(1)。
代码丑拒贴,(ˉ▽ ̄~)
C. 数组分拆 DP
题意:
给出一个长度为(N(N leq 10^5))的数组(A_1 sim A_N,|Ai| leq 100),求有多少种方案可以将数组划分若干段,且每段之和不为0,方案数对(10^9 + 7)取模。
分析:
设(d_i)为将前(i)个数划分为若干段且每段之和不为(0)的方案数
容易想到一个(O(N^2))的做法:
预处理一下前缀和(sum_i= sumlimits_{j=1}^i A_j)
对于前(j)个数的某个划分,如果(sum_i-sum_j
eq 0)的话,就可以将(A_{j+1} sim A_i)作为后缀拼在后面得到一个前(i)个数的划分
所以转移方程为:(d_i = sumlimits_{j=0}^{i-1} d_j, sum_i - sum_{j-1}
eq 0)
优化:(d_i)是由(d_j)拼接上不为(0)的后缀得到的,不如反向考虑,把所有可能的方案加起来然后减去那些后缀为(0)的不合法的方案。
设(cnt_s)表示元素之和为(s)的划分个数,如果某些划分和为(sum_i),说明它对应的后缀为(0)是不合法的,所以不合法的划分就有(cnt_{sum_i})个。
因此(d_i=sumlimits_{j=1}^{i-1} d_j - cnt_{sum_i})
边界情况是(d_0=1),对应(A_1 sim A_i)作为一个整体划分的转移
#include <cstdio>
#include <map>
using namespace std;
typedef long long LL;
const LL MOD = 1000000007LL;
void add(LL& a, LL b) { a += b; if(a >= MOD) a -= MOD; }
map<int, LL> M;
const int maxn = 100000 + 10;
int n, a[maxn], sum[maxn];
LL d[maxn];
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", a + i);
sum[i] = sum[i - 1] + a[i];
}
M[0] = 1;
d[0] = 1;
LL tot = 1;
for(int i = 1; i <= n; i++) {
d[i] = (tot + MOD - M[sum[i]]) % MOD;
add(M[sum[i]], d[i]);
add(tot, d[i]);
}
printf("%lld
", d[n]);
return 0;
}
D. 矩形计数 容斥原理
题意:
有一个(N)行(M)列的矩形,其中有(K)个黑格子,其余的都是白格子。
求全部由白格子组成的矩形的个数。
分析:
首先不考虑黑格子,计算出一共有多少个矩形:
枚举矩形的大小(r imes c),这样大小的矩形一共有((N-r+1)(M-c+1))个。
然后减去不符合要求的矩形,也就是减去包含第一个黑格子的矩形个数,减去包含第二个黑格子,第三个的……
然后再加上包含第一第二黑格子的矩形数……
也就是容斥原理。
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
int n, m, k;
int r[10], c[10];
int main()
{
scanf("%d%d%d", &n, &m, &k);
LL ans = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
ans += (LL)(n + 1 - i) * (m + 1 - j);
for(int i = 0; i < k; i++)
scanf("%d%d", r + i, c + i);
for(int S = 1; S < (1 << k); S++) {
int lft = m + 1, top = n + 1;
int down = 0, rgh = 0;
int cnt = 0;
for(int i = 0; i < k; i++) if((S >> i) & 1) {
cnt++;
lft = min(lft, c[i]);
top = min(top, r[i]);
rgh = max(rgh, c[i]);
down = max(down, r[i]);
}
int sign = (cnt & 1) ? -1 : 1;
ans += (LL)sign * lft * (m + 1 - rgh) * top * (n + 1 - down);
}
printf("%lld
", ans);
return 0;
}