2022“杭电杯”中国大学生算法设计超级联赛(4) 题解
A. Link with Bracket Sequence II#
设 表示区间 有多少个匹配方案。
对于 类情况,有
对于 类情况,有 ,其中 的情况由 和 决定,取值范围为 。
发现直接dp,对于 类会有重复情况,对于串 的dp值,我们可能分别从 和 递推过来,会导致重复计数。
我们将上述两种情况分开,并规定只能从右边接新的括号串,于是就可以继续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#
设 表示为每个地方需要放多少个魔法原料, 表示 的前缀和。
对于每个 ,有
对于每个 ,有
对于每个 ,有
对这些不等式转化并进行差分约束求最长路即可求得 的最小值。
#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;
}
D. Link with Equilateral Triangle#
猜结论,输出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;
}
E. Link with Level Editor II#
发现 的值较小,考虑用邻接矩阵求解从 到 之间长度为 的方案数。具体做法自行百度“矩阵乘法求路径方案数”。
发现需要找一段连续的区间满足题目要求,考虑用双指针找出一段连续区间。发现我们不好处理除法操作。
考虑如何规避这个除法操作,我们利用对顶栈的技巧。
设一个 作为两个栈的栈顶, 为 到 的累乘值,再设 为 到 的累乘值。每次查询 到 时,我们只需要求得 的值即可。
#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#
如果下一层能直接杀死怪物我们就直接跳到下一层,
如果不能我们应跳到一个最小的位置,使得从这个位置往下走能杀死所有怪物。
具体方法是维护一个 值,表示最多还需多少攻击力杀死怪物。
直接往上维护这个值,当 值小于等于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 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~