日常训练2025-1-12

日常训练2025-1-12

P2679 [NOIP2015 提高组] 子串

普及+/提高

https://www.luogu.com.cn/problem/P2679

思路

https://www.luogu.com.cn/article/k0zkdin9

评述

做DP时可以把能想到的有用的状态都定义出来,后序在把不需要的,或者可以根据其他状态推出来的状态删除,逐渐优化。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 1000000007
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n, m, k;
	std::cin >> n >> m >> k;

	std::string a, b;
	std::cin >> a >> b;

	a = ' ' + a;
	b = ' ' + b;

	std::vector dp(m+1, std::vector<std::vector<int>>(k+1, std::vector<int>(2)));
	std::vector ndp(m+1, std::vector<std::vector<int>>(k+1, std::vector<int>(2)));	

	dp[0][0][0] = 1;
	ndp[0][0][0] = 1;

	for (int i = 1; i <= n; i++){
		ndp = dp;
		for (int j = 1; j <= m; j++){
			for (int p = 1; p <= k; p++){
				if (a[i] == b[j]){
					dp[j][p][1] = (ndp[j-1][p][1] + ndp[j-1][p-1][1]) % MOD + ndp[j-1][p-1][0] % MOD;
					dp[j][p][1] %= MOD;
					dp[j][p][0] = (ndp[j][p][1] + ndp[j][p][0]) % MOD;
				}else{
					dp[j][p][1] = 0;
					dp[j][p][0] = (ndp[j][p][1] + ndp[j][p][0]) % MOD;
				}
			}
		}
	}

	std::cout << (dp[m][k][0] + dp[m][k][1]) % MOD << '\n';
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

E - Takahashi is Slime 2

rating:绿色

https://atcoder.jp/contests/abc384/tasks/abc384_e

思路

题目的几个关键点

  1. 每次肯定是找相邻的权值最小的点扩展,如果最小的点都已经无法扩展了,那么就没有点可以扩展了。这个过程有点想Dijstra算法,所以想到要使用优先级队列
  2. 判断大小只需要用已经有的 ans / x 向上取整就行,必须严格比这个值小。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n, m, x;
	std::cin >> n >> m >> x;
	int sx, sy;
	std::cin >> sx >> sy;
	std::vector d(n+1, std::vector<i64>(m+1));
	int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
	std::vector vis(n+1, std::vector<int>(m+1, 0));

	for (int i = 1; i <= n; i++){
		for (int j = 1; j <= m; j++){
			std::cin >> d[i][j];
		}
	}

	i64 ans = d[sx][sy];
	vis[sx][sy] = 1;
	std::priority_queue<std::pair<i64, std::array<int, 2>>, std::vector<std::pair<i64, std::array<int, 2>>>, std::greater<>> q;
	for (int i = 0; i < 4; i++){
		int dx = sx + dir[i][0];
		int dy = sy + dir[i][1];
		if (dx <= 0 || dx > n || dy <= 0 || dy > m || vis[dx][dy] == 1) continue;
		q.push({d[dx][dy], {dx, dy}});
		vis[dx][dy] = 1;
	}
	while (!q.empty()){
		auto [w, arr] = q.top();
		q.pop();
		// std::cout << w << ' ' << arr[0] << ' ' << arr[1] << '\n';
		if (w < ((ans+x-1)/x)){
			ans += w;
			for (int i = 0; i < 4; i++){
				int dx = arr[0] + dir[i][0];
				int dy = arr[1] + dir[i][1];
				if (dx <= 0 || dx > n || dy <= 0 || dy > m || vis[dx][dy] == 1) continue;
				q.push({d[dx][dy], {dx, dy}});
				vis[dx][dy] = 1;
			}
		}else{
			break;
		}
	}

	std::cout << ans << '\n';

}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

P1855 榨取kkksc03

rating:普及

https://www.luogu.com.cn/problem/P1855

思路(经典的01背包问题)

/*
	记住核心:每个物品只能选一个时从大体积枚举到小体积,每个物品可以选无数次时从小体积枚举到大体积。
 	f[i][j][k]:表示前i个愿望中,有j块钱和k的时间的情况下能实现的愿望数量的最大值。
 	初始化:全0
 	每个愿望有不实现和实现两种情况
	状态转移:f[i][j][k] = max(f[i-1][j][k], f[i-1][j-m[i]][k-t[i]] + 1);
	由于第一维只有i和i-1所以可以优化掉
*/

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

/*
 	f[i][j][k]:表示前i个愿望中,有j块钱和k的时间的情况下能实现的愿望数量的最大值。
 	初始化:全0
 	每个愿望有不实现和实现两种情况
	状态转移:f[i][j][k] = max(f[i-1][j][k], f[i-1][j-m[i]][k-t[i]] + 1);
	由于第一维只有i和i-1所以可以优化掉
*/
void solve(){
	int n, M, T;
	std::cin >> n >> M >> T;

	std::vector<int> m(n+1), t(n+1);
	for (int i = 1; i <= n; i++){
		std::cin >> m[i] >> t[i];
	}

	std::vector f(M+1, std::vector<int>(T+1));
	for (int i = 1; i <= n; i++){
		for (int j = M; j >= m[i]; j--){
			for (int k = T; k >= t[i]; k--){
				f[j][k] = std::max(f[j][k], f[j-m[i]][k-t[i]] + 1);
			}
		}
	}

	std::cout << f[M][T] << '\n';
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

P1775 石子合并(弱化版)

https://www.luogu.com.cn/problem/P1775

思路(区间DP)

/*
	状态定义:f[i][j]表示i...j这个区间合并能得到的最小代价
	初始化:f[i][i] = 0,当区间内只有自己时代价为0
	状态转移:f[i][j] = min(f[i][j], f[i][k] + f[k+1][j] + sum[j] - sum[i-1]);通过枚举k这个分界线来计算。
*/

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n;
	std::cin >> n;

	std::vector<int> v(n+1), pre(n+1);
	std::vector f(n+1, std::vector<int>(n+1, INF));
	for (int i = 1; i <= n ; i++){
		std::cin >> v[i];
		pre[i] = pre[i-1] + v[i];
		f[i][i] = 0;
	}


	for (int l = 2; l <= n; l++){
		for (int i = 1; i <= n - l + 1; i++){
			int j = i + l - 1;
			for (int k = 1; k < j; k++){
				f[i][j] = std::min(f[i][j], f[i][k] + f[k+1][j] + pre[j] - pre[i-1]);
			}
		}
	}

	std::cout << f[1][n] << '\n';
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

C. Trip to the Olympiad

rating:1500

https://codeforces.com/contest/2057/problem/C

思路(按位考虑+Trick)

分析题目的暗示的性质,按位考虑

https://www.bilibili.com/video/BV1mnrRYGEiu/?spm_id_from=333.337.search-card.all.click&vd_source=4a339d299e165d8fe38b9926c5240eae

代码1

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int l, r;
	std::cin >> l >> r;

	int ans = 0;
	int k = 30;
	while ((r >> k & 1) == (l >> k & 1)){
		ans += (r >> k & 1) << k;
		k--;
	}

	ans += 1 << k;

	if (ans - 1 == l){
		std::cout << l << ' ' << ans << ' ' << ans + 1 << '\n';
	}else{
		std::cout <<l << ' ' << ans  << ' ' << ans - 1<< '\n';
	}
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	std::cin >> t;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

代码2

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef basic_string<int> BI;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
mt19937 mrand(random_device{}()); 
const ll mod=1000000007;
int rnd(int x) { return mrand() % x;}
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
// head

int l,r;
void solve() {
	scanf("%d%d",&l,&r);
	for (int b=30;b>=0;b--) if ((l>>b)!=(r>>b)) {
		int g=(r>>b)<<b;
		if (r==g) printf("%d %d %d\n",g,g-1,g-2);
		else printf("%d %d %d\n",g+1,g,g-1);
		break;
	}
}
int _;
int main() {
	for (scanf("%d",&_);_;_--) {
		solve();
	}
}

posted @ 2025-01-12 11:11  califeee  阅读(23)  评论(0)    收藏  举报