2025-03-24~04-06 hetao1733837 的刷题记录

2025-03-24~04-06 hetao1733837 的刷题记录

03-24

LGP1736 创意吃鱼法

原题链接:创意吃鱼法

分析

第一反应是搜索......但是,怎么搜呢?爆搜必然超时,难道记忆化一下变成 DP?咦,确实比较可以,因为我们额外记录一维方向似乎比较可以做。但是,我怎么感觉还是会超时呢?写了一下,16pts ......AI 给出了每个方向处理一下的方法,但是我不知道区别在哪......

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2505;
int n, m;
int a[N][N];
int up[N][N], l[N][N], r[N][N];
int dp1[N][N], dp2[N][N];
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++){
            cin >> a[i][j];
            if (a[i][j] == 0) 
                up[i][j] = up[i - 1][j] + 1;
            else 
                up[i][j] = 0;
            if (a[i][j] == 0) 
                l[i][j] = l[i][j - 1] + 1;
            else 
                l[i][j] = 0;
        }
    }
    for (int i = 1; i <= n; i++){
        for (int j = m; j >= 1; j--){
            if (a[i][j] == 0) 
                r[i][j] = r[i][j + 1] + 1;
            else 
                r[i][j] = 0;
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++){
            if (a[i][j] == 1){
                dp1[i][j] = min({dp1[i - 1][j - 1], up[i - 1][j], l[i][j - 1]}) + 1;
                ans = max(ans, dp1[i][j]);
            }
        }
    }
    for (int i = 1; i <= n; i++){
        for (int j = m; j >= 1; j--){
            if (a[i][j] == 1){
                dp2[i][j] = min({dp2[i - 1][j + 1], up[i - 1][j], r[i][j + 1]}) + 1;
                ans = max(ans, dp2[i][j]);
            }
        }
    }
    cout << ans;
}

03-27

LGP10785 [NOI2024] 集合

原题链接:[NOI2024] 集合

分析

你别说,还真是 hash 。难道说只要在短时间内判断......好吧,似乎不是那么简单的。

云浅是怎么场切的/ll

并未理解。

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200005, M = 600005;
int n, m, q;
int a[N][3], b[N][3];
mt19937_64 rnd(chrono::system_clock::now().time_since_epoch().count());
int buc[N], ap[M], bp[M], suma, sumb, base;
int c[N];
int gethash(int x){
	if (!x)
		return x;
	x ^= base;
	x ^= x << 7;
	x ^= x >> 11;
	x ^= 13;
	return x;
}
void Hash(int id, int d){
	for (auto i : a[id]){
		suma -= gethash(ap[i]);
		if (d > 0)
			ap[i] += buc[id];
		else
			ap[i] -= buc[id];
		suma += gethash(ap[i]);
	}
	for (auto i : b[id]){
		sumb -= gethash(bp[i]);
		if (d > 0)
			bp[i] += buc[id];
		else
			bp[i] -= buc[id];
		sumb += gethash(bp[i]);
	}
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m >> q;
	for (int i = 1; i <= n; i++){
		cin >> a[i][0] >> a[i][1] >> a[i][2];
	}
	for (int i = 1; i <= n; i++){
		cin >> b[i][0] >> b[i][1] >> b[i][2];
	}
	for (int i = 1; i <= n; i++){
		c[i] = n;
	}
	int tmp = 10;
	while (tmp--){
		base = rnd();
		for (int i = 1; i <= n; i++)
			buc[i] = rnd();
		Hash(1, 1);
		for (int l = 1, r = 1; l <= n; Hash(l, -1), l++){
			while (r <= n && suma == sumb){
				r++;
				if (r <= n)
					Hash(r, 1);
			}
			c[l] = min(c[l], r - 1);
		}
	}
	for (int cs = 1, l, r; cs <= q; cs++){
		cin >> l >> r;
		if (r <= c[l])
			cout << "Yes" << '\n';
		else
			cout << "No" << '\n';
	}
}

04-06

LGP4186 [USACO18JAN] Cow at Large G

原题链接:[USACO18JAN] Cow at Large G

分析

多久没刷题了自己心里清楚。

闲下来的时候想想 BO 停课的时候 OI 怎么办。

还有,你的求导和微积分打算什么时候学?

我绝对写过类似的东西!

咦,现在居然只有一个暴露的位置了吗?

为啥我感觉......这么微妙呢?为啥我感觉封住所有出口即可?怎么证明呢?不,可以提出反例。

所以,我们设 d p i dp_{i} dpi 表示 Bessie 在 i i i 子树的根节点位置暴露所需的最少农夫数。

思考一下,似乎 Bessie 向上走是不优的,因为我们可以把另一棵子树封住,这就是另一个状态了。

考虑把 K K K 当作整棵树的根节点。

考虑转移。

是......所有子树之和吗?

这么简单?我不信。

好吧,23 pts

思索一下题解......显然,一个农夫可以控制的点是确定的,所以,我们把这些点标出来,最终使得牛的所有路径被封死即可。

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, k, fa[N], de[N], ans;
bool son[N], tag[N];
vector<int> e[N];
void dfs1(int u, int pa){
    son[u] = true;
    fa[u] = pa;
    de[u] = de[pa] + 1;
    for (int i = 0; i < (int)e[u].size(); i++){
        int v = e[u][i];
        if (v != pa){
            son[u] = false;
            dfs1(v, u);
        }
    }
}
void dfs2(int u, int pa){
    if (tag[u]){
        ans++;
        return ;
    }
    for (int i = 0; i < (int)e[u].size(); i++){
        int v = e[u][i];
        if (v != pa){
            dfs2(v, u);
        }
    }
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> k;
    fa[k] = 0;
    de[0] = -1;
    for (int i = 1, u, v; i < n; i++){
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs1(k, 0);
    for (int i = 1; i <= n; i++){
        if (son[i]){
            int acc = i;
            for (int j = 1; j <= de[i] / 2; j++){
                acc = fa[acc];
            }
            tag[acc] = 1;
        }
    }
    dfs2(k, 0);
    cout << ans;
}

LGP5307 [COCI 2018/2019 #6] Mobitel

原题链接:[COCI 2018/2019 #6] Mobitel

分析

mhh 推的一看就是好题,题面如此短小精悍!

感觉有搞头啊......就是设某一个矩形,然后转移比较好搞。

我上个厕所......

初步思路是设 d p i , j dp_{i,j} dpi,j 表示以 ( i , j ) (i,j) (i,j) 为右下角的矩形的答案数。

那么,转移显然,即
d p i , j = d p i − 1 , j + d p i , j − 1 dp_{i,j}=dp_{i-1,j}+dp_{i,j-1} dpi,j=dpi−1,j+dpi,j−1

果真如此?

那,怎么玩剩下的呢?怎么确定初始的?难道,终究还是要写 d f s dfs dfs 了吗?理论上还是要搜/ll

还要求 O ( r s ) O(rs) O(rs) ......这个很微妙,证明刚才那个思路是有可行性的。

但是,我还是不会/(ㄒoㄒ)/~~......


咋是分块!

整除分块......好像听过......但是我不会啊/ll

显然,我们之前的方程需要再加上一维得 d p i , j , k dp_{i,j,k} dpi,j,k 表示位置 ( i , j ) (i,j) (i,j) 乘积为 k k k 的路径数,基本无法转移,即使可以也会爆炸。

那么,设 d p i , j , k dp_{i,j,k} dpi,j,k 表示位置 ( i , j ) (i,j) (i,j) 再乘上 k k k,路径乘积才会超过 n n n 得方案数,状态量过于大了。

我们发现, k k k 这一维的取值很少,故整除分块,有 O ( n ) O(\sqrt{n}) O(n ) 种取值,最终复杂度 O ( r s n ) O(rs\sqrt{n}) O(rsn )。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define mod 1000000007
using namespace std;
const int RS = 305, N = 1000005, M = 2505;
int r, c, n, a[RS][RS];
int calc(int x, int y){
    return (x + y - 1) / y; 
}
int d[N];
int di[N], tp, rv[N];
int dp[2][RS][M];
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> r >> c >> n;
    for (int i = 1; i <= r; i++){
        for (int j = 1; j <= c; j++){
            cin >> a[i][j];
        }
    }
    for (int i = 1; i <= n; i++){
        d[i] = calc(n, i);
        if (d[i] != d[i - 1]){
            di[++tp] = d[i];
            rv[d[i]] = tp;
        }
    }
    dp[1][1][rv[calc(n, a[1][1])]] = 1;
    for (int i = 1; i <= r; i++){
        for (int j = 1; j <= c; j++){
            for (int k = 1; k <= tp; k++){
                int cur = dp[i & 1][j][k];
                if (cur == 0) 
                    continue;
                if (i != r){
                    int nxt = rv[calc(di[k], a[i + 1][j])];
                    dp[(i & 1) ^ 1][j][nxt] = (dp[(i & 1) ^ 1][j][nxt] + cur) % mod;
                }
                if (j != c){
                    int nxt = rv[calc(di[k], a[i][j + 1])];
                    dp[i & 1][j + 1][nxt] = (dp[i & 1][j + 1][nxt] + cur) % mod;
                }
                if (i != r || j != c || k != tp){
                    dp[i & 1][j][k] = 0;
                }
            }
        }
    }
    cout << dp[r & 1][c][tp] << '\n';
    return 0;
}
相关推荐
_深海凉_2 小时前
LeetCode热题100-环形链表
算法·leetcode·链表
原来是猿2 小时前
Linux进程信号详解(三):信号保存
开发语言·c++·算法
2401_892070982 小时前
算法与数据结构精讲:最大子段和(暴力 / 优化 / 分治)+ 线段树从入门到实战
c++·算法·线段树·最大子段和
memcpy02 小时前
LeetCode 904. 水果成篮【不定长滑窗+哈希表】1516
算法·leetcode·散列表
老四啊laosi2 小时前
[双指针] 8. 四数之和
算法·leetcode·四数之和
汀、人工智能2 小时前
[特殊字符] 第24课:反转链表
数据结构·算法·链表·数据库架构··反转链表
田梓燊2 小时前
leetcode 41
数据结构·算法·leetcode
暴力求解3 小时前
C++ ---- String类(一)
开发语言·c++
_深海凉_3 小时前
LeetCode热题100-三数之和
算法·leetcode·职场和发展