2022“杭电杯”中国大学生算法设计超级联赛(4) 题解

dp[i][j] 表示区间 [i,j] 有多少个匹配方案。

对于 AB 类情况,有 dp[i][j]=dp[i][k]dp[k+1][j]

对于 (A) 类情况,有 dp[i][j]=dp[i+1][j1]k,其中 k 的情况由 a[i]a[j] 决定,取值范围为 0, 1, m

发现直接dp,对于AB 类会有重复情况,对于串 ABC 的dp值,我们可能分别从 A+BCAB+C 递推过来,会导致重复计数。

我们将上述两种情况分开,并规定只能从右边接新的括号串,于是就可以继续dp了。

#include<bits/stdc++.h>
#define int long long
#define eps 1e-12
using namespace std;

const int MAXN = 500 + 5;
const int MOD = 1e9 + 7;

int n, m, a[MAXN], dp[3][MAXN][MAXN];

void Solve() {
	scanf("%lld%lld", &n, &m);
	for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
	for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) dp[0][i][j] = dp[1][i][j] = 0;
	for(int i = 1; i < n; i++) {
		if(a[i] == 0 && a[i + 1] == 0) dp[0][i][i + 1] = m;
		else if(a[i] == -a[i + 1] && a[i] > 0) dp[0][i][i + 1] = 1;
		else if( (!a[i] && a[i + 1] < 0) || (!a[i + 1] && a[i] > 0) ) dp[0][i][i + 1] = 1;
		else dp[0][i][i + 1] = 0; 
	}
	for(int k = 3; k <= n; k++) {
		for(int i = 1; i + k - 1 <= n; i++) {
			int j = i + k - 1, res0 = 0, res1 = 0;
			for(int l = i + 1; l + 1 < j; l++) {
				res1 = (res1 + (dp[0][i][l] + dp[1][i][l]) * dp[0][l + 1][j] % MOD) % MOD;
			}
			
			if(a[i] == 0 && a[j] == 0) res0 = (res0 + m * (dp[0][i + 1][j - 1] + dp[1][i + 1][j - 1]) % MOD) % MOD;
			else if(a[i] == -a[j] && a[i] > 0) res0 = (res0 + (dp[0][i + 1][j - 1] + dp[1][i + 1][j - 1]) ) % MOD;
			else if( (!a[i] && a[j] < 0) || (!a[j] && a[i] > 0) ) res0 = (res0 + (dp[0][i + 1][j - 1] + dp[1][i + 1][j - 1]) ) % MOD;
			
			dp[0][i][j] = res0;
			dp[1][i][j] = res1;
		}
	}
	/*
	for(int i = 1; i <= n; i++) { 
		for(int j = 1; j <= n; j++) cout << dp[1][i][j] << " "; cout << "dp\n";
	}
	*/
	printf("%lld\n", (dp[0][1][n] + dp[1][1][n]) % MOD);
}

signed main()
{
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; scanf("%lld", &T);
	while(T--) Solve();
	return 0;
}

C. Magic#

a[i] 表示为每个地方需要放多少个魔法原料,sum[i] 表示 a[i] 的前缀和。

对于每个 p[i],有 a[ik]+a[ik+1]+...+a[i+k]=sum[i+k]sum[ik1]p[i]

对于每个 l,r,b,有 a[l]+a[l+1]+...+a[r]=sum[r]sum[l1]b

对于每个 sum[i],有 a[i]=sum[i]sum[i1]0

对这些不等式转化并进行差分约束求最长路即可求得 sum[n] 的最小值。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pii pair<int, int>
using namespace std;

const int MAXN = 1e4 + 5;
const int INF = 0x3f3f3f3f;

vector<pii> G[MAXN];
int n, k, q, dis[MAXN], vis[MAXN], cnt[MAXN], ans;
queue<int> Q; 

void Add(int u, int v, int w) {
	G[u].push_back({v, w});
}

bool SPFA(int s) {
	for(int i = 0; i <= n; i++) dis[i] = -INF, vis[i] = cnt[i] = 0;
	dis[s] = 0, vis[s] = 1; Q.push(s);
	while(!Q.empty()) {
		int u = Q.front(); Q.pop();
		vis[u] = 0;
		for(auto i : G[u]) {
			int v = i.fi, w = i.se;
			if(dis[v] < dis[u] + w) {
				dis[v] = dis[u] + w;
				cnt[v] = cnt[u] + 1;
				if(cnt[v] >= n + 1) return 0;
				if(!vis[v]) Q.push(v), vis[v] = 1;
			}
		}
	} 
	return 1;
}

void Solve() {
	cin >> n >> k; k--; n++;
	for(int i = 1; i < n; i++) {
		int p, l, r; cin >> p;
		r = min(n - 1, i + k), l = max(0ll, i - k - 1);
		int u = r, v = l, w = p;
		Add(v + 1, u + 1, w);
	}
	cin >> q;
	for(int i = 1; i <= q; i++) {
		int l, r, b; cin >> l >> r >> b;
		int u = l - 1, v = r, w = -b;
		Add(v + 1, u + 1, w);
	}
	for(int i = 1; i <= n; i++) Add(i - 1, i, 0);
	if(!SPFA(0)) ans = -1;
	else ans = dis[n];
	cout << ans << "\n";
	
	for(int i = 0; i <= n; i++) G[i].clear();
}

signed main()
{
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T;
	while(T--) Solve();
	return 0;
}

猜结论,输出No即可。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
#define pir make_pair
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;
const int MAXN = 8e5 + 5;

signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T;
	while(T--) {
		int n; cin >> n;
		cout << ("No\n");
	}
	return 0;
}

发现 m 的值较小,考虑用邻接矩阵求解从 1m 之间长度为 k 的方案数。具体做法自行百度“矩阵乘法求路径方案数”。

发现需要找一段连续的区间满足题目要求,考虑用双指针找出一段连续区间。发现我们不好处理除法操作。

考虑如何规避这个除法操作,我们利用对顶栈的技巧。

设一个 p 作为两个栈的栈顶,curf[p+1]f[r] 的累乘值,再设 g[i]f[i]f[p] 的累乘值。每次查询 f[l]f[r] 时,我们只需要求得 g[i]cur 的值即可。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pii pair<int, int>
using namespace std;
const int MAXN = 5000 + 5;
const int INF = 0x3f3f3f3f;

int n, m, k;

struct Matrix {
	int c[21][21];
	
	Matrix operator * (const Matrix& b) const {
		Matrix res;
		for(int i = 1; i <= m; ++i) for(int j = 1; j <= m; ++j) res.c[i][j] = 0;
		for(int k = 1; k <= m; ++k) {
			for(int i = 1; i <= m; ++i) {
				for(int j = 1; j <= m; ++j) {
					res.c[i][j] += c[i][k] * b.c[k][j];
				}
			}
		}
		return res;
	}

	void init(int x) {
		for(int i = 1; i <= m; ++i) for(int j = 1; j <= m; ++j) c[i][j] = 0;
		for(int i = 1; i <= m; i++) c[i][i] = x;
	}
}f[MAXN], g[MAXN];

bool Check(Matrix a, Matrix b) {
	int res = 0;
	for(int i = 1; i <= m; i++) {
		res += a.c[1][i] * b.c[i][m];
		if(res > k) return 0;
	}
	return 1;
}

void Solve() {
	cin >> n >> m >> k;
	for(int i = 1; i <= n; ++i) f[i].init(1);
	for(int i = 1; i <= n; ++i) {
		int l; cin >> l;
		for(int j = 1; j <= l; ++j) {
			int u, v; cin >> u >> v;
			f[i].c[u][v] = 1;
		}
	}
	int maxx = -1; g[0].c[1][m] = INF;
	Matrix cur; cur.init(1);
	for(int l = 0, r = 1, p = 0; r <= n; ++r) {
		cur = cur * f[r];
		while( !Check(g[l], cur) ) {
			l++;
			if(l > p) {
				cur.init(1);
				g[r] = f[r];
				for(int i = r - 1; i > p; i--) g[i] = f[i] * g[i + 1];
				p = r;
			}
		}
		maxx = max(maxx, r - l + 1);
	}
	cout << maxx << "\n";
}

signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T;
	while(T--) Solve();
	return 0;
}

F. BIT Subway#

签到,根据每次买的票直接对题意模拟即可。

#include<bits/stdc++.h>
#define int long long
#define eps 1e-12
using namespace std;

const int MAXN = 1e5 + 5;

int n;
double a[MAXN], b[MAXN];

double min(double a, double b) {
	return a < b ? a : b;
}

void Solve() {
	scanf("%lld", &n);
	for(int i = 1; i <= n; i++) scanf("%lf", &a[i]), b[i] = a[i];
	double c1 = 0, c2 = 0;
	for(int i = 1; i <= n; i++) {
		if(c1 < 100.0) {
			double minn = min(a[i], 100.0 - c1);
			a[i] -= minn;
			c1 += minn;
		}
		if((100.0 < c1 || abs(c1 - 100.0) < eps) && c1 < 200.0) {
			double minn = min(a[i] * 0.8, (200 - c1) );
			a[i] -= minn / 0.8;
			c1 += minn;
		} 
		if(c1 > 200.0 || abs(c1 - 200.0) < eps) {
			c1 += 0.5 * a[i];
		}
	}
	for(int i = 1; i <= n; i++) {
		if(c2 < 100) c2 += b[i];
		else if(c2 < 200) c2 += 0.8 * b[i];
		else c2 += 0.5 * b[i];
	}
	
	printf("%.3lf %.3lf\n", c1, c2);
}

signed main()
{
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; scanf("%lld", &T);
	while(T--) Solve();
	return 0;
}

G. Climb Stairs#

如果下一层能直接杀死怪物我们就直接跳到下一层,

如果不能我们应跳到一个最小的位置,使得从这个位置往下走能杀死所有怪物。

具体方法是维护一个 need 值,表示最多还需多少攻击力杀死怪物。

直接往上维护这个值,当 need 值小于等于0时即这个位置合法,更新位置即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int MAXN = 1e6 + 5;
const int MOD = 1e9 + 7;

int n, m, a[MAXN], k;

void Solve() {
	cin >> n >> a[0] >> k;
	for(int i = 1; i <= n; i++) cin >> a[i];
	int p = 1, c = k, flag = 1;
	while(p <= n && flag) {
		//cout << p << " p\n";
		if(a[p] <= a[0]) {
			a[0] += a[p];
			c = k; p++;
		} else {
			int i = 1, need = a[p] - a[0], cur = 0;
			while(i < c && p + i - 1 < n) {
				i++;
				need = max(need - a[p + i - 1], a[p + i - 1] - a[0]);
				if(need <= 0) {
					cur = 1;
					for(int j = p; j < p + i; j++) a[0] += a[j];
					p = p + i;
					c = k - i + 1;
					break;
				}
			}
			if(!cur) flag = 0;
		}
	}
	cout << (flag ? "YES" : "NO") << "\n";
}

signed main()
{
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; scanf("%lld", &T);
	while(T--) Solve();
	return 0;
}

作者:Orzjh

出处:https://www.cnblogs.com/Orzjh/p/16546227.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Orzjh  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu