20260713总结
总结
- 多多调试,因为你想到的方法很可能是正解。
- 锻炼调题能力,不然在一道题上耗时太久。
- 多看题,先打好一定能拿到的部分分再去写其他题的正解。
- 不要认为你的分数已经够了就开始摆烂,因为你会做的别人也会。
T1 排列鞋子
T2 P3459 [POI 2007] MEG-Megalopolis
场切了,没有思维,全是码量。
考场上坚持调试这一题,虽然最后过了但是调试这一题的时间太久了,导致放弃了T4。
思路
首先注意到题目中给出的是一棵树。初始每一条边权为 \(1\) ,那么修改边相当于将边权置为 \(0\)。
考虑把这棵树拍到 dfn 序上,并记录每个节点的深度。
由于每一棵子树在 dfn 序列中都是连续的。所以修改和查询操作相当于区间修改和单点查询。
考虑用线段树维护深度。
代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 250005;
int cnt, n, m, dep[N], dfn[N], dfn2[N];
struct node
{
int l, r;
} a[N];
struct tree
{
int data, lzy;
} w[N << 2];
vector<int> g[N];
bool InRange(int l, int r, int L, int R) { return (l >= L) && (r <= R); }
bool OutofRange(int l, int r, int L, int R) { return (r < L) || (l > R); }
void pushup(int u)
{
w[u].data = w[u * 2].data + w[u * 2 + 1].data;
}
void pushdown(int u, int l, int r)
{
if (w[u].lzy == 0)
return;
w[u * 2].lzy += w[u].lzy, w[u * 2 + 1].lzy += w[u].lzy;
int mid = l + r >> 1;
w[u * 2].data -= (mid - l + 1) * w[u].lzy;
w[u * 2 + 1].data -= (r - mid) * w[u].lzy;
w[u].lzy = 0;
}
void build(int u, int l, int r)
{
if (l == r)
{
w[u].data = dep[l];
return;
}
int mid = l + r >> 1;
build(u * 2, l, mid);
build(u * 2 + 1, mid + 1, r);
pushup(u);
}
void update(int u, int l, int r, int L, int R)
{
if (InRange(l, r, L, R))
{
w[u].lzy++;
w[u].data -= r - l + 1;
return;
}
if (OutofRange(l, r, L, R))
return;
int mid = l + r >> 1;
pushdown(u, l, r);
update(u * 2, l, mid, L, R);
update(u * 2 + 1, mid + 1, r, L, R);
pushup(u);
}
int query(int u, int l, int r, int L, int R)
{
if (InRange(l, r, L, R))
return w[u].data;
if (OutofRange(l, r, L, R))
return 0;
int mid = l + r >> 1;
pushdown(u, l, r);
return query(u * 2, l, mid, L, R) + query(u * 2 + 1, mid + 1, r, L, R);
}
int dfs(int x, int fa, int sum)
{
a[x] = {++cnt, cnt};
dfn[x] = cnt;
dfn2[cnt] = x;
dep[cnt] = sum;
for (int v : g[x])
{
if (v == fa)
continue;
a[x].r = dfs(v, x, sum + 1);
}
return a[x].r;
}
signed main()
{
// freopen("meg.in", "r", stdin);
// freopen("meg.out", "w", stdout);
cin >> n;
for (int i = 1; i < n; i++)
{
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
// cin >> dep[i];
}
dfs(1, 0, 0);
build(1, 1, n);
// for (int i = 1; i <= n; i++)
// {
// cout << a[i].l << " " << a[i].r << endl;
// }
// for (int i = 1; i <= n; i++)
// {
// cout << dfn[i] << " ";
// }
// cout << endl;
// for (int i = 1; i <= n; i++)
// {
// cout << query(1, 1, n, dfn[a[i].l], dfn[a[i].l]) << " ";
// }
cin >> m;
for (int i = 1; i <= n + m - 1; i++)
{
char op;
cin >> op;
if (op == 'W')
{
int x;
cin >> x;
cout << query(1, 1, n, dfn[x], dfn[x]) << endl;
// cout << dep[dfn[x]] << endl;
}
if (op == 'A')
{
int x, y;
cin >> x >> y;
update(1, 1, n, a[y].l, a[y].r);
// for (int i = 1; i <= n; i++)
// {
// cout << query(1, 1, n, a[i].l, a[i].l) << " ";
// }
// cout << endl;
}
}
}
T3 P3558 [POI 2013] BAJ-Bytecomputer
这道题之前考过,但是由于我之前的懒惰,一直都没有做这道题。
很好的一道分类讨论题。
思路
动态规划
猜测结论:按照优策略操作完后序列中只有 \(0\)、\(1\)、\(-1\)。
设 \(dp[i][j]\) 为第 \(i\) 位变为 \(j - 1\) (防止数组下标为负),且序列合法时最小的操作次数。
初始化:\(dp[1][a[1] + 1]\) 为 \(0\),其他全为 \(inf\)。
然后就从 \(dp[2]\) 开始转移,分讨:
- \(a[i] = -1\)
因为 \(a[i]\) 已经是 \(-1\) 了,所以 \(dp[i][0] = dp[i - 1][0]\)。
当 \(a[i - 1]\) 初始为 \(1\) 时,\(a[i]\) 才有可能变为 \(0\),所以 \(dp[i][1] = min(dp[i - 1][0], dp[i - 1][1]) + 1\)。否则为 \(inf\)。
同理,\(dp[i][2] = min(dp[i - 1][0], min(dp[i - 1][1], dp[i - 1][2])) + 2\)。但由于前一个数可以变成一了,所以当 \(a[i - 1] != 1\) 时,\(dp[i][2] = dp[i - 1][2] + 2\)
- \(a[i] = 0\) 和 \(a[i] = 1\) 时同理。
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
const int inf = 1e9;
int dp[N][3], a[N], n, ans = inf;
signed main()
{
scanf("%lld", &n);
for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
if(a[1] == -1) dp[1][1] = inf, dp[1][2] = inf;
if(a[1] == 0) dp[1][0] = inf, dp[1][2] = inf;
if(a[1] == 1) dp[1][0] = inf, dp[1][1] = inf;
for(int i = 2; i <= n; i ++)
{
if(a[i] == -1)
{
dp[i][0] = dp[i - 1][0];
if(a[i - 1] == 1) dp[i][1] = min(dp[i - 1][0], dp[i - 1][1]) + 1;
else dp[i][1] = inf;
if(a[i - 1] == 1) dp[i][2] = min(dp[i - 1][0], min(dp[i - 1][1], dp[i - 1][2])) + 2;
else dp[i][2] = dp[i - 1][2] + 2;
}
if(a[i] == 0)
{
dp[i][0] = dp[i - 1][0] + 1;
dp[i][1] = min(dp[i - 1][0], dp[i - 1][1]);
if (a[i - 1] == 1) dp[i][2] = min(dp[i - 1][0], min(dp[i - 1][1], dp[i - 1][2])) + 1;
else dp[i][2] = dp[i - 1][2] + 1;
}
if(a[i] == 1)
{
dp[i][0] = dp[i - 1][0] + 2;
if(a[i - 1] == -1) dp[i][1] = min(dp[i - 1][0], dp[i - 1][1]) + 1;
else dp[i][1] = dp[i - 1][0] + 1;
dp[i][2] = min(dp[i - 1][0], min(dp[i - 1][1], dp[i - 1][2]));
}
}
for(int i = 0; i <= 2; i ++)
{
ans = min(ans, dp[n][i]);
}
// printf("%lld %lld %lld\n", dp[n][0], dp[n][1], dp[n][2]);
if(ans >= inf) cout << "BRAK" << endl;
else cout << ans << endl;
}
贪心
还有个贪心解法的,但是我还没有学懂。
T4 P9525 [JOIST 2022] 团队竞技 / Team Contest
这一道题我做过的,考场上也想出了正解的东西。但是写完代码发现答案不对之后就没有调试了(当时代码实现方式不对导致特别复杂难调)
思路
这题不是一眼反悔贪心吗。
考虑各选出三只某个能力值最大的海狸(尽管会有重复)。然后记录每一只海狸的编号,如果编号有重复,那么就换成一只能力值次大的。
一直重复上述步骤,直到选出的三只海狸合法。
如果直到最后都没有选出来,则没有合法方案。
发现可以用三个优先队列维护海狸的每种能力值。
代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, a[N], b[N], c[N], vis[N];
priority_queue<pair<int, int> > A, B, C;
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i] >> b[i] >> c[i];
A.push(make_pair(a[i], i));
B.push(make_pair(b[i], i));
C.push(make_pair(c[i], i));
}
for (int i = 1; i <= n; i++)
{
int x = A.top().second, y = B.top().second, z = C.top().second;
if (b[x] == b[y] || c[x] == c[z])
{
vis[x] = 1;
}
if (c[y] == c[z] || a[y] == a[x])
{
vis[y] = 1;
}
if (a[z] == a[x] || b[z] == b[y])
{
vis[z] = 1;
}
if (!vis[x] && !vis[y] && !vis[z])
{
cout << a[x] + b[y] + c[z];
return 0;
}
while (!A.empty() && vis[A.top().second])
{
A.pop();
}
while (!B.empty() && vis[B.top().second])
{
B.pop();
}
while (!C.empty() && vis[C.top().second])
{
C.pop();
}
}
cout << -1;
}

浙公网安备 33010602011771号