2024杭电多校第10场
10#
1001 LIS (hdu7541)#
网络流总诱惑我去学一些根本做不出来的东西
由Dilworth定理,偏序集能划分成的最小全序集个数等于最大反链的长度。将 \((i, a_i)\) 视为一个二元组,定义 \((i,a_i)\leq(j,a_j)\) 当且仅当 \(i\leq j, a_i\geq a_j\),则 \(i\leq j\) 且 \(a_i<a_j\) 时,两元素不可比,此时形成的反链为单调上升的子序列。题目要求最长上升子序列长度 \(\leq k\),即等价于序列中不交叉的不降子序列不多于 \(k\) 个。为了使删数代价最小,需要用 \(k\) 个不降子序列尽可能覆盖代价更大的元素。
考虑费用流建模,将 \(i\) 拆成 \(2i - 1, 2i\) 两个节点,连接源点 \(st\) 与 \(2i-1\),汇点 \(ed\) 与 \(2i\),由于每个点只能选择一次,流量为 \(1\),费用为 \(0\);\(2i-1\) 与 \(2i\) 连边,流量为 \(1\),费用为 \(-b_i\),将最大覆盖价值转换为最小费用求解。对于 \(i < j\) 且 \(a_i>a_j\),连接 \(2i\) 与 \(2j-1\),流量为 \(1\),费用为 \(0\),若此处有流量经过、则代表它们在同一个子序列内。题目要求计算 \(1\leq k\leq n\) 的所有答案,因为所有边的流量限制为 \(1\),每次增广的流量也一定为 \(1\),在最短路计算、边权修改之后直接输出本次答案即可,不需要重新计算每个最大流下的最小费用。
#include <bits/stdc++.h> #define ll long long using namespace std; typedef pair <int, int> P; const int N = 510; const ll INF = 1e12; int n, a[N], b[N]; int st, ed; struct edge{ int t, w, c, r; }; vector <edge> v[N * 2]; void Clear() { for(int i = 0; i <= ed; i++) { v[i].clear(); } } void add(int f, int t, int w, int c) { v[f].push_back({t, w, c, (int)v[t].size()}); v[t].push_back({f, 0, -c, (int)v[f].size() - 1}); } ll d[N * 2], h[N * 2]; int pre[N * 2], id[N * 2]; // Primal-Dual 原始对偶算法 void dijkstra() { fill(d, d + ed + 1, INF); d[st] = 0; priority_queue <P, vector<P>, greater<P> > q; q.push(P(d[st], st)); while(!q.empty()) { int x = q.top().first, y = q.top().second; q.pop(); if(d[y] < x) continue; for(int i = 0; i < v[y].size(); i++) { int t = v[y][i].t, w = v[y][i].w, c = v[y][i].c; if(w > 0 && d[t] > d[y] + c + h[y] - h[t]) { d[t] = d[y] + c + h[y] - h[t]; pre[t] = y; id[t] = i; q.push(P(d[t], t)); } } } } int ans; void mcf() { dijkstra(); for(int i = st; i <= ed; i++) { h[i] += d[i]; } for(int i = ed; i != st; i = pre[i]) { edge &e = v[pre[i]][id[i]]; ans += e.c; e.w--; v[i][e.r].w++; } // 一次增广后直接返回(实测每次重新算就会TLE) } void solve() { Clear(); scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); } int sum = 0; for(int i = 1; i <= n; i++) { scanf("%d", &b[i]); sum += b[i]; } fill(h, h + ed + 1, 0); st = 0, ed = n * 2 + 1; for(int i = 1; i <= n; i++) { // 预处理初始势能 h[i * 2 - 1] = h[i * 2 - 2]; h[i * 2] = h[i * 2 - 1] - b[i]; add(st, i * 2 - 1, 1, 0); add(i * 2 - 1, i * 2, 1, -b[i]); add(i * 2, ed, 1, 0); } h[ed] = h[n * 2]; for(int i = 1; i <= n; i++) { for(int j = i + 1; j <= n; j++) { if(a[j] < a[i]) { add(i * 2, j * 2 - 1, 1, 0); } } } ans = 0; for(int f = 1; f <= n; f++) { mcf(); // ans为最大覆盖价值的相反数 printf("%d ", sum + ans); } printf("\n"); } int main() { int t; scanf("%d", &t); while(t--) { solve(); } system("pause"); return 0; }
1002 scenery (hdu7542)#
由于 \(l\) 序列不增、 \(r\) 序列不降,每处景色的拍摄安排在可选时间的开始/结束位置显然是最优的。设 \(dp[i][j]\) 表示(从后往前)考虑到第 \(i\) 处景色、可选时间从 \(j\) 开始的最晚结束位置,则转移方程:
\(dp[i][max(l_i, j) + t_i] = max(dp[i][max(l_i, j) + t_i], dp[i - 1][j])\) (开始位置)
\(dp[i][j] = max(dp[i][j], min(dp[i - 1][j], r_i + 1) - t_i)\) (结束位置)
预先将所有状态赋为不合法情况 \(-1\),最后检查 \(dp[1][j]\) 中是否有合法情况即可。
memset(dp, -1, sizeof(dp)); int it = 1; dp[1][0] = m + 1; for(int i = n; i; i--) { it ^= 1; for(int j = 0; j <= m; j++) { dp[it][j] = -1; } for(int j = 0; j <= m; j++) { int l = max(a[i].l - 1, j) + a[i].t; if(l > a[i].r || l >= dp[it ^ 1][j]) continue; dp[it][l] = max(dp[it][l], dp[it ^ 1][j]); } for(int j = 0; j <= m; j++) { int r = min(dp[it ^ 1][j], a[i].r + 1) - a[i].t; if(r < a[i].l || r <= j) continue; dp[it][j] = max(dp[it][j], r); } } bool flag = 0; for(int j = 0; j <= m; j++) { if(dp[it][j] >= 0) { flag = 1; break; } } if(flag) printf("YES\n"); else printf("NO\n");
居然能在赛时写对dp,挺难得的)
1008 SunBian (hdu7548)#
对于 \(n\) 为偶数的情况,除非Alice能够一次将所有笋变成横向,即 \(k=n\),否则无论她如何操作,Bob都可以在对称位置模仿操作、使自己必胜。\(n\) 为奇数时,若 \(1<k<n\),Bob可将整个环隔开成为两个对称的部分、同理模仿操作;\(k=1\) 或 \(n\) 时则Bob必败。
1010 A+B Problem (hdu7550)#
一个小规律:\(ans_i\) 的二进制最低位与 \(ans_{i - 1}\) 无关,对于确定的 \(a_i,b_i\),\(ans_i\space \&\space 1\)为定值。而其他每位上的取值只与 \(a_i,b_i\) 以及 \(ans_{i - 1}\) 的较低位有关,因此确定最低位后、可通过递推求出完整的 \(ans_i\),即最终答案唯一。
for(int i = 1; i <= q; i++) { scanf("%lld%lld", &a[i], &b[i]); c[i] = (a[i] ^ b[i]) & 1; } c[0] = c[q]; for(int z = 1; z < 32; z++) { for(int i = 1; i <= q; i++) { ll y = (a[i] ^ c[i - 1]) + (b[i] ^ c[i - 1]); c[i] += (y & (1ll << z)); } c[0] = c[q]; } for(int i = 1; i <= q; i++) { printf("%lld\n", c[i]); }
另有:写1011的时候假设这位同学每次比赛都不幸爆零、排他前面的人都AK,后来发现题解真这么写的,好一道吉利的题目()
作者:Aderose_yr
出处:https://www.cnblogs.com/meowqwq/p/18373888
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
喵喵喵)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现