SCOI 2015 Day1 简要题解

「SCOI2015」小凸玩矩阵

题意

一个 N×MNM )的矩阵 A,要求小凸从其中选出 N 个数,其中任意两个数字不能在同一行或同一列,现小凸想知道选出来的 N 个数中第 K 大的数字的最小值是多少。

1KNM250,1Ai,j109

题解

一道简单的网络流题。

不难发现第 K 大和第 NK+1 小是本质一样的。

所以就是要使得第 NK+1 小尽量小,那么我们二分这个最小值 min 就行了。

然后我们对于 Ai,jmin 的点 (i,j) 考虑,如果能从中选出至少 NK+1 个点就是合法的。

然后直接上二分图建模就行了。

Hungray 可以跑过去 ,但是更喜欢 Dinic 的复杂度。

所以最后复杂度就是 O(N2logA) 的,轻松通过,甚至能跑更大范围。

代码

#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("2006.in", "r", stdin); freopen ("2006.out", "w", stdout); #endif } const int N = 255, inf = 0x3f3f3f3f; int n, m, k, a[N][N]; template<int Maxn, int Maxm> struct Dinic { int Head[Maxn], Next[Maxm], to[Maxm], cap[Maxm], e; void Init() { Set(Head, 0); e = 1; } inline void add_edge(int u, int v, int flow) { to[++ e] = v; Next[e] = Head[u]; cap[e] = flow; Head[u] = e; } inline void Add(int u, int v, int flow) { add_edge(u, v, flow); add_edge(v, u, 0); } int S, T, dis[Maxn]; bool Bfs() { queue<int> Q; Q.push(S); Set(dis, 0); dis[S] = 1; while (!Q.empty()) { int u = Q.front(); Q.pop(); for (int i = Head[u], v = to[i]; i; v = to[i = Next[i]]) if (cap[i] && !dis[v]) dis[v] = dis[u] + 1, Q.push(v); } return dis[T]; } int cur[Maxn]; int Dfs(int u, int flow) { if (!flow || u == T) return flow; int res = 0, f; for (int& i = cur[u], v = to[i]; i; v = to[i = Next[i]]) if (dis[v] == dis[u] + 1 && (f = Dfs(v, min(flow, cap[i])))) { res += f; cap[i] -= f; cap[i ^ 1] += f; if (!(flow -= f)) break ; } return res; } inline int Run() { int res = 0; while (Bfs()) Cpy(cur, Head), res += Dfs(S, inf); return res; } }; Dinic<N * 2, N * N * 2> T; bool Check(int lim) { T.Init(); T.S = n + m + 1, T.T = n + m + 2; For (i, 1, n) For (j, 1, m) if (a[i][j] <= lim) T.Add(i, j + n, 1); For (i, 1, n) T.Add(T.S, i, 1); For (i, 1, m) T.Add(i + n, T.T, 1); return T.Run() >= k; } int main () { File(); n = read(); m = read(); k = n - read() + 1; int l = inf, r = -inf; For (i, 1, n) For (j, 1, m) a[i][j] = read(), chkmin(l, a[i][j]), chkmax(r, a[i][j]); int ans = 0; while (l <= r) { int mid = (l + r) >> 1; if (Check(mid)) ans = mid, r = mid - 1; else l = mid + 1; } printf ("%d\n", ans); return 0; }

「SCOI2015」国旗计划

题意

给你一个长为 M 的环,共有 N 个互不包含的环上区间 [Ci,Di] (当 Ci>Di 的时候相当于 [Ci,M][1,Di] 拼成的一个区间)。

每次强制选择第 i 个区间,询问至少还要选择多少个区间才能满足所有点都被覆盖。

N2×105,M<109,1Ci,DiM

题解

如果是序列上,那么就是很简单的一道倍增题了。

只需要处理一下每个点被跨过的区间 [li,ri] 的最大的 ri 就行了,因为每次都向尽量走的远。

然后预处理第 i 号点走 2j 步能到达的最远点 toi,j 就行了。每次查询直接倍增就行了。

环上的麻烦一点,首先倍长拆环。然后你有可能你会绕环走了好几圈,这个你就多记下一个走了 2j 步走了的距离 disi,j 就行了。(因为这样比较好写)

每次要走的距离分类讨论就行了。

然后最后的复杂度就是 O(NlogN) 的,轻松通过。

其实可以把这个倍增结构放到树上,每次就只需要查 dis 的差就行了(如果不在子树内要 +1 ),复杂度可以优化成 O(N) 的。

代码

#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__) #define pb push_back using namespace std; typedef long long ll; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("2007.in", "r", stdin); freopen ("2007.out", "w", stdout); #endif } const int N = 4e5 + 1e3; ll dis[N * 2][21]; int to[N * 2][21], l[N], r[N]; vector<int> V[N * 2]; int n, m, Hash[N * 2], len; inline int Get_Id(int x) { return lower_bound(Hash + 1, Hash + len + 1, x) - Hash; } int main () { File(); n = read(); m = read(); For (i, 1, n) { Hash[++ len] = l[i] = read(); Hash[++ len] = r[i] = read(); } sort(Hash + 1, Hash + len + 1); len = unique(Hash + 1, Hash + len + 1) - Hash - 1; For (i, 1, n) { l[i] = Get_Id(l[i]), r[i] = Get_Id(r[i]); if (l[i] > r[i]) V[1].pb(r[i]), V[l[i]].pb(r[i] + len); else V[l[i]].pb(r[i]); } int cur = 0; For (i, 1, len) { for (int v : V[i]) chkmax(cur, v); dis[i][0] = dis[i + len][0] = cur - i; to[i][0] = to[i + len][0] = cur; } int Lim = ceil(log2(len << 1)); For (j, 1, Lim) For (i, 1, len << 1) { to[i][j] = to[to[i][j - 1]][j - 1]; dis[i][j] = dis[i][j - 1] + dis[to[i][j - 1]][j - 1]; } For (i, 1, n) { int ans = 0, u = r[i], need = l[i] <= r[i] ? len - (r[i] - l[i]) : l[i] - r[i]; Fordown (j, Lim, 0) if (dis[u][j] < need) need -= dis[u][j], u = to[u][j], ans |= 1 << j; printf ("%d%c", ans + 2, i == iend ? '\n' : ' '); } return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/10017112.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(267)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示