2025-12-21~22 hetao1733837的刷题笔记
12-21
HDU4565 So Easy!
原题链接:So Easy!
分析
个人认为,先化简再说。
终于找到一篇人能看的题解了。能粘过来吗?HDU4565 So Easy! 矩阵快速幂
行,和我说的一样,上取整和根号太丑了,我们尝试去掉。
数据范围中给出了 ( a − 1 ) 2 < b < a 2 (a-1)^2 < b < a^2 (a−1)2<b<a2,则 a − 1 < b < a a-1<\sqrt{b}<a a−1<b <a,所以 a − b < 1 a-\sqrt{b} < 1 a−b <1, ( a − b ) n < 1 (a-\sqrt{b})^n < 1 (a−b )n<1。
设 C n = ( a − b ) n + ( a + b ) n C_n=(a-\sqrt{b})^n + (a+\sqrt{b})^n Cn=(a−b )n+(a+b )n,则 S n = ⌈ C n ⌉ S_n=\left\lceil{C_n}\right\rceil Sn=⌈Cn⌉。
指数很大,直接递归显然 TLE,尝试优化,很直接地想到矩阵加速,则需要递推关系。
我们把 C n C_n Cn 乘上一个 2 a 2a 2a,虽然我也不知道为什么,则
2 a × C n = ( 2 a + b − b ) × C n = ( a + b ) × C n + ( a − b ) × C n = C n + 1 + ( a 2 − b ) × C n − 1 2a\times C_n = (2a+\sqrt{b}-\sqrt{b})\times C_n\\ =(a+\sqrt{b})\times C_n+(a-\sqrt{b})\times C_n\\ =C_{n+1}+(a^2-b)\times C_{n-1} 2a×Cn=(2a+b −b )×Cn=(a+b )×Cn+(a−b )×Cn=Cn+1+(a2−b)×Cn−1
因此
C n = 2 a × C n − 1 + ( b − a 2 ) × C n − 2 C_n=2a\times C_{n-1}+(b-a^2)\times C_{n-2} Cn=2a×Cn−1+(b−a2)×Cn−2
这里矩阵就很明显了,直接上正解。
正解
cpp
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2;
int n, mod, a, b;
int f[N], mat[N][N];
void mulself(int a[N][N]){
int c[N][N];
memset(c, 0, sizeof(c));
for(int i = 0; i < N; i++){
for(int j = 0; j < N; j++){
for(int k = 0; k < N; k++){
c[i][j] = (c[i][j] + (long long)a[i][k] * a[k][j]) % mod;
}
}
}
memcpy(a, c, sizeof(c));
}
void mul(int f[N], int a[N][N]){
int c[N];
memset(c, 0, sizeof(c));
for(int i = 0; i < N; i++){
for(int j = 0; j < N; j++){
c[i] = (c[i] + (long long)f[j] * a[j][i]) % mod;
}
}
memcpy(f, c, sizeof(c));
}
int main(){
while (scanf("%d%d%d%d", &a, &b, &n, &mod) != EOF){
double sqrt_b = sqrt(1.0 * b);
f[0] = (long long)ceil((1LL * a * a % mod + b) % mod + 2.0 * a * sqrt_b) % mod;
f[1] = (2LL * a) % mod;
if(n == 1){
printf("%d\n", f[1]);
continue;
}
if(n == 2){
printf("%d\n", f[0]);
continue;
}
mat[0][0] = (2LL * a) % mod;
mat[0][1] = 1;
mat[1][0] = ((b - 1LL * a * a) % mod + mod) % mod;
mat[1][1] = 0;
n -= 2;
while(n){
if (n & 1)
mul(f, mat);
mulself(mat);
n >>= 1;
}
printf("%d\n", f[0] % mod);
}
}
行,我得在洛谷上交道题。
LG3957 [NOIP 2017 普及组] 跳房子
原题链接:[NOIP 2017 普及组] 跳房子
分析
HT 给的精简版题意有点不当人了。还是自己读题吧。
不是一眼 D P DP DP 吗?先来设暴力转移方程。
啊?HT 的 PPT 这么不当人?哦,我不是人。
行,我们发现我没发现,对于一个 g 可以使得最终收益 ≥ k \ge k ≥k,则必有 g+1 使得收益 ≥ k \ge k ≥k,因此,g 与收益是否 ≥ k \ge k ≥k 单调,考虑二分出 g。
那么,我们会得出一个 g ,问题转化为给定区间 [ m a x ( 1 , l ) , r ] [max(1,l),r] [max(1,l),r],求收益的最大可能值。
设 f i f_i fi 表示从起点到达 i i i 位置的最大可能收益。转移显然:
f i = s i + max j = i − r i − l f j f_i=s_i+\max\limits_{j=i-r}^{i-l}{f_j} fi=si+j=i−rmaxi−lfj
区间最值,上单调队列。注意 x i x_i xi 的范围较大,应该离散化。我试试能不能自己写出来。
失误了,离散化改变了数值,会使得后续距离无法控制。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500005;
const int INF = -1e18;
int n, d, k;
struct node{
int x, s;
}a[N];
int f[N], q[N];
bool check(int g){
int L = max(1ll, d - g);
int R = d + g;
for (int i = 1; i <= n; i++)
f[i] = INF;
f[0] = 0;
int head = 0, tail = -1;
int j = 0;
for (int i = 1; i <= n; i++){
while (j < i && a[i].x - a[j].x >= L){
if (f[j] > INF){
while (head <= tail && f[q[tail]] <= f[j]){
tail--;
}
q[++tail] = j;
}
j++;
}
while (head <= tail && a[i].x - a[q[head]].x > R){
head++;
}
if (head <= tail){
f[i] = f[q[head]] + a[i].s;
}
}
for (int i = 1; i <= n; i++){
if (f[i] >= k){
return true;
}
}
return false;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> d >> k;
a[0].x = 0;
a[0].s = 0;
int sum = 0;
for (int i = 1; i <= n; i++){
cin >> a[i].x >> a[i].s;
if (a[i].s > 0)
sum += a[i].s;
}
if (sum < k){
cout << -1;
return 0;
}
int l = 0, r = max(a[n].x, d);
int ans = -1;
while (l <= r){
int mid = (l + r) >> 1;
if (check(mid)){
ans = mid;
r = mid - 1;
}
else{
l = mid + 1;
}
}
cout << ans;
}
我好菜/ll
根据 DeepSeek 建议,我先补 CF 了。
CF 似了......
AT_abc242_h [ABC242Ex] Random Painting
原题链接1:[ABC242Ex] Random Painting
原题链接2:Ex - Random Painting
分析
啊?我真得应该好好学期望的/ll
不是,啥意思?
被击败了/ll
行,了解了 min-max 容斥,初步理解了。
设 t i t_i ti 表示 i i i 位置第一次被染色的时间,则题目转化为:求 E [ max 1 ≤ i ≤ n t i ] E[\max\limits_{1\le i\le n}{t_i}] E[1≤i≤nmaxti]。
使用 min-max 容斥得到
E [ max i ∈ S t i ] = ∑ T ⊆ S , T ≠ ∅ ( − 1 ) ∣ T ∣ + 1 E [ min i ∈ T t i ] E[\max\limits_{i\in S}{t_i}]=\sum\limits_{T\subseteq S,T\neq \varnothing}{(-1)^{|T|+1}E[\min\limits_{i\in T}{t_i}]} E[i∈Smaxti]=T⊆S,T=∅∑(−1)∣T∣+1E[i∈Tminti]
那么,如何求取 E [ min i ∈ T t i ] E[\min\limits_{i\in T}{t_i}] E[i∈Tminti] 呢?
如果能算出与 T T T 有交的区间 [ L i , R i ] [L_i,R_i] [Li,Ri] 的个数 x x x,那么,其概率为 x m \frac{x}{m} mx,一旦选中立即结束,则 E [ min ( T ) ] = m x E[\min(T)]=\frac{m}{x} E[min(T)]=xm,记这个 x x x 为 f ( T ) f(T) f(T)。
对 [ L i , R i ] [L_i,R_i] [Li,Ri] 前缀和预处理,设 d p i , j dp_{i,j} dpi,j 表示前 i i i 个数中选子集,且必须选 i i i, f ( T ) = j f(T)=j f(T)=j 的方案数,枚举上一个选的位置 D P DP DP 即可。
这是啥?我要 he!
正解
cpp
#include <bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
const int N = 405;
int n, m;
int f[N][N], dp[N][N];
int qpow(int a, int b){
int res = 1;
while (b){
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int inv(int a){
return qpow(a, mod - 2) % mod;
}
void add(int &x, int v){
x += v;
if (x >= mod)
x -= mod;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1, l, r; i <= m; i++){
cin >> l >> r;
f[l][r]++;
}
for (int i = n; i >= 1; i--){
for (int j = i; j <= n; j++){
f[i][j] += f[i][j - 1] + f[i + 1][j] - f[i + 1][j - 1];
}
}
dp[0][0] = mod - 1;
for (int i = 1; i <= n; i++){
for (int j = 0; j <= m; j++){
for (int k = 0; k < i; k++){
if (j >= f[k + 1][i - 1]){
add(dp[i][j], mod - dp[k][j - f[k + 1][i - 1]]);
}
}
}
}
for (int j = 0; j <= m; j++){
for (int k = 1; k <= n; k++){
if (j >= f[k + 1][n]){
add(dp[n + 1][j], dp[k][j - f[k + 1][n]]);
}
}
}
int ans = 0;
assert(dp[n + 1][m] == 0);
for (int j = 0; j < m; j++)
add(ans, 1ll * m * inv(m - j) % mod * dp[n + 1][j] % mod);
cout << ans;
}
在这粘一个 DeepSeek 给的详解。
似乎可以理解,又似乎没完全理解。
12-22
LG3389 【模板】高斯消元法
原题链接:【模板】高斯消元法
分析
这玩意是个纯纯的人类智慧啊?
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
const double eps = 1e-7;
double a[N][N];
int n;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n + 1; j++){
cin >> a[i][j];
}
}
for (int i = 1; i <= n; i++){
int maxn = i;
for (int j = i + 1; j <= n; j++){
if (abs(a[j][i]) > abs(a[maxn][i])){
maxn = j;
}
}
for (int j = 1; j <= n + 1; j++){
swap(a[i][j], a[maxn][j]);
}
if (abs(a[i][i]) < eps){
cout << "No Solution";
return 0;
}
for (int j = n + 1; j >= 1; j--){
a[i][j] = a[i][j] / a[i][i];
}
for (int j = 1; j <= n; j++){
if (j != i){
double tmp = a[j][i] / a[i][i];
for (int k = 1; k <= n + 1; k++){
a[j][k] -= a[i][k] * tmp;
}
}
}
}
for (int i = 1; i <= n; i++)
cout << fixed << setprecision(2) << a[i][n + 1] << '\n';
}
POJ 终于活了😭
POJ1681 Painter's Problem
原题链接:Painter's Problem
分析
巧妙的转化,由于仅存在两种颜色,考虑转化到二进制上更好表示。设 x i = 0 / 1 x_i=0/1 xi=0/1 表示 i 号砖被刷了 0 / 1 0/1 0/1 次(刷更多次显然是没有意义的)。
定义 a i , j a_{i,j} ai,j 表示第 i i i 块砖和第 j j j 块砖之间的关系,若 a i , j = 1 a_{i,j}=1 ai,j=1 表示 i i i 与 j j j 是上下左右相邻的,否则不相邻。特别地, a i , i = 1 a_{i,i}=1 ai,i=1。
则问题转化为求
{ a 1 , 1 × x 1 ⨁ a 1 , 2 × x 2 ⨁ ⋅ ⋅ ⋅ ⨁ a 1 , k × x k ⨁ s 1 = 0 a 2 , 1 × x 1 ⨁ a 2 , 2 × x 2 ⨁ ⋅ ⋅ ⋅ ⨁ a 2 , k × x k ⨁ s 2 = 0 ⋮ a k , 1 × x 1 ⨁ a k , 2 × x 2 ⨁ ⋅ ⋅ ⋅ ⨁ a k , k × x k ⨁ s k = 0 \begin{cases} a_{1,1}\times x_1 \bigoplus a_{1,2}\times x_2\bigoplus ··· \bigoplus a_{1,k}\times x_k \bigoplus s_1=0\\ a_{2,1}\times x_1 \bigoplus a_{2,2}\times x_2\bigoplus ··· \bigoplus a_{2,k}\times x_k \bigoplus s_2=0\\ \vdots \\ a_{k,1}\times x_1 \bigoplus a_{k,2}\times x_2\bigoplus ··· \bigoplus a_{k,k}\times x_k \bigoplus s_k=0 \end{cases} ⎩ ⎨ ⎧a1,1×x1⨁a1,2×x2⨁⋅⋅⋅⨁a1,k×xk⨁s1=0a2,1×x1⨁a2,2×x2⨁⋅⋅⋅⨁a2,k×xk⨁s2=0⋮ak,1×x1⨁ak,2×x2⨁⋅⋅⋅⨁ak,k×xk⨁sk=0
其中 ⨁ \bigoplus ⨁ 表示按位异或。
高消求解即可。
正解
cpp
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 405, M = 20;
int dx[] = {0, 0, 1, -1},
dy[] = {1, -1, 0, 0};
int a[N][N], e[M][M], x[N];
void init(){
memset(x, 0, sizeof(x));
memset(a, 0, sizeof(a));
}
bool check(int x, int y, int n){
if (x <= 0 || x >= n + 1 || y <= 0 || y >= n + 1)
return false;
return true;
}
int gauss(int n){
int mx, col, k, i, j;
for (int l = 1; l <= n; l++){
for (int o = 1; o <= n; o++){
i = (l - 1) * n + o;
j = (l - 1) * n + o;
a[i][n * n + 1] = e[l][o];
a[i][j] = 1;
for (int p = 0; p < 4; p++){
int tx = l + dx[p], ty = o + dy[p];
if (check(tx, ty, n)){
j = (tx - 1) * n + ty;
a[i][j] = 1;
}
}
}
}
col = k = 1;
while (k <= n * n && col <= n * n){
mx = k;
for (int u = k; u <= n * n; u++){
if (a[u][col]){
mx = u;
break;
}
}
if (a[mx][col] != 0){
if (mx != k){
for (int u = 0; u <= n * n + 1; u++){
swap(a[mx][u], a[k][u]);
}
}
for (int u = k + 1; u <= n * n; u++){
if (a[u][col]){
for (int l = 0; l <= n * n + 1; l++){
a[u][l] ^= a[k][l];
}
}
}
k++;
}
col++;
}
for (int u = k; u <= n * n; u++){
for (int v = 1; v <= n * n + 1; v++){
if (a[u][v]){
return -1;
}
}
}
int res = 0;
for (int l = n * n; l >= 1; l--){
x[l] = a[l][n * n + 1];
for (int o = l + 1; o <= n * n; o++){
if (a[l][o]){
x[l] ^= x[o];
}
}
if (x[l] == 1)
res++;
}
return res;
}
int T, n;
int main(){
cin >> T;
while (T--){
cin >> n;
init();
char tmp[M][M];
for (int i = 0; i < n; i++){
cin >> tmp[i];
}
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
if (tmp[i][j] == 'y')
e[i + 1][j + 1] = 0;
else
e[i + 1][j + 1] = 1;
}
}
int ans = gauss(n);
if (ans == -1){
cout << "inf" << '\n';
continue;
}
cout << ans << '\n';
}
}
这是啥啊/ll
LG2152 [SDOI2009] SuperGCD
原题链接:[SDOI2009] SuperGCD
分析
可笑吗?我只看到了 star 模拟赛。呃,辗转相除,取模相当于减,应该就是这吧......
呃,并非,但是我快蕾丝了,我得躺会,下午再学吧。先粘个正解。
我要回教室睡觉!
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, m, k, a[N], b[N], g[N], ans[N];
string s1, s2;
bool check(){
if ((n == 1 && a[1] == 0) || (m == 1 && b[1] == 0))
return false;
return true;
}
void write(int *x, int len){
for (int i = len; i >= 1; i--)
putchar(x[i] ^ 48);
return ;
}
void mul(int *x, int &len){
bool flag = false;
for (int i = 1; i <= len; i++){
x[i] <<= 1;
if (flag)
x[i] |= 1;
if (x[i] >= 10){
x[i] -= 10;
flag = true;
}
else{
flag = false;
}
}
if (flag){
++len;
x[len] = 1;
}
return ;
}
void divide(int *x, int &len){
int k = 0;
int res[N];
for (int i = len; i >= 1; i--){
if (x[i] & 1){
int cur = ((k * 10 + x[i]) >> 1) << 1;
res[i] = (k * 10 + x[i]) >> 1;
k = (k * 10 + x[i]) - cur;
}
else{
res[i] = (k * 10 + x[i]) >> 1;
k = 0;
}
}
for (int i = len; i >= 1; i--){
if (res[i] == 0)
len--;
else
break;
}
if (len == 0)
len++;
for (int i = 1; i <= len; i++)
x[i] = res[i];
return ;
}
bool cmp(){
if (n < m)
return false;
if (n > m)
return true;
for (int i = n; i >= 1; i--){
if (a[i] < b[i])
return false;
if (a[i] > b[i])
return true;
}
return true;
}
void solve(int *x, int &len1, int *y, int &len2){
for (int i = 1; i <= len2; i++){
x[i] -= y[i];
if (x[i] < 0){
--x[i + 1];
x[i] += 10;
}
}
if (len1 == 1)
return ;
while (len1 > 0 && x[len1] == 0)
len1--;
if (len1 == 0)
len1 = 1;
return ;
}
void modify(int *x, int &len1, int *y, int &len2){
len1 = len2;
for (int i = 1; i <= len1; i++)
x[i] = y[i];
return ;
}
int len, cnt;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> s1 >> s2;
n = s1.length();
m = s2.length();
s1 = " " + s1;
s2 = " " + s2;
g[++k] = 1;
for (int i = 1, j = n; i <= n; i++, j--){
a[j] = s1[i] - '0';
}
for (int i = 1, j = m; i <= m; i++, j--){
b[j] = s2[i] - '0';
}
while (check()){
if (a[1] % 2 == 0 && b[1] % 2 == 0){
divide(a, n);
divide(b, m);
cnt++;
}
else if (a[1] % 2 == 0 && (b[1] & 1)){
divide(a, n);
}
else if ((a[1] & 1) && b[1] % 2 == 0){
divide(b, m);
}
else{
if (!cmp())
solve(b, m, a, n);
else
solve(a, n, b, m);
}
}
if (cmp())
modify(ans, len, a, n);
else
modify(ans, len, b, m);
for (int i = 1; i <= cnt; i++)
mul(ans, len);
write(ans, len);
}
这都是啥?
中午吃的叉烧饭给哥们吃恶心了,晚上换点别的吃。
LG2455 [SDOI2006] 线性方程组
原题链接:[SDOI2006] 线性方程组
分析
无法理解的模板......
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
const double eps = 1e-9;
double a[N][N];
int n;
bool eq(double x, double y){
return fabs(x - y) < eps;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n + 1; j++){
cin >> a[i][j];
}
}
int nwline = 1;
for (int k = 1; k <= n; k++){
int maxi = nwline;
for (int i = nwline + 1; i <= n; i++){
if (fabs(a[i][k]) > fabs(a[maxi][k])){
maxi = i;
}
}
if (eq(a[maxi][k], 0.0)){
continue;
}
for (int j = 1; j <= n + 1; j++){
swap(a[nwline][j], a[maxi][j]);
}
for (int i = 1; i <= n; i++){
if (i == nwline)
continue;
double mul = a[i][k] / a[nwline][k];
for (int j = k; j <= n + 1; j++){
a[i][j] -= a[nwline][j] * mul;
}
}
nwline++;
}
if (nwline <= n){
for (int i = nwline; i <= n; i++){
if (!eq(a[i][n + 1], 0.0)){
cout << -1;
return 0;
}
}
cout << 0;
return 0;
}
for (int i = 1; i <= n; i++){
double x = a[i][n + 1] / a[i][i];
if (eq(a[i][n + 1], 0.0))
cout << "x" << i << "=" << "0.00" << '\n';
else
cout << "x" << i << "=" << fixed << setprecision(2) << x << '\n';
}
}
HDU5755 Gambler Bo
原题链接:Gambler Bo
分析
都说是板子,但是我不会......
在 cnblogs 只找到了三篇博客讲这题。
都是啥啊/ll
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 35, M = 1225;
int T, n, m, nn, b[N][N], a[M][M];
int got(int x, int y){
return (x - 1) * m + y;
}
void guass(){
for (int i = 1; i <= nn; i++){
int r = i;
for (int j = i; j <= nn; j++){
if (a[j][i]){
r = j;
break;
}
}
if (!a[r][i]){
if (a[r][nn + 1])
assert(1 == 0);
else
continue;
}
for (int j = i; j <= nn + 1; j++)
swap(a[r][j], a[i][j]);
for (int j = 1; j <= nn; j++){
if (i == j || !a[j][i])
continue;
int tmp = (a[j][i] == a[i][i]) ? 1 : 2;
for (int k = 1; k <= nn + 1; k++){
a[j][k] = (a[j][k] * tmp - a[i][k] + 3) % 3;
}
}
}
}
int main(){
cin >> T;
while (T--){
cin >> n >> m;
nn = n * m;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
cin >> b[i][j];
}
}
memset(a, 0, sizeof(a));
for (int x = 1, i = 1; x <= n; x++){
for (int y = 1; y <= m; y++, i++){
a[i][nn + 1] = (3 - b[x][y]) % 3;
}
}
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
a[got(i, j)][got(i, j)] = 2;
if (j < m)
a[got(i, j + 1)][got(i, j)] = 1;
if (j > 1)
a[got(i, j - 1)][got(i, j)] = 1;
if (i < n)
a[got(i + 1, j)][got(i, j)] = 1;
if (i > 1)
a[got(i - 1, j)][got(i, j)] = 1;
}
}
guass();
int ans = 0;
for (int i = 1; i <= nn; i++)
if (a[i][nn + 1])
ans += a[i][nn + 1] * a[i][i] % 3;
printf("%d\n", ans);
for (int x = 1, i = 1; x <= n; x++){
for (int y = 1; y <= m; y++, i++){
if (a[i][nn + 1]){
int cnt = a[i][nn + 1] * a[i][i] % 3;
while (cnt--){
printf("%d %d\n", x, y);
}
}
}
}
}
}