「JSOI2014」学生选课

传送门

看到这题首先可以二分。

考虑对于当前的 \(mid\) 如何 \(\text{check}\)

我们用 \(f_{i,j}\) 来表示 \(i\) 对 \(j\) 的好感度排名,那么对于两个人 \(i\),\(j\) 如果有 \(\max\{f_{i, j}, f_{j, i}\} > mid\) 那么显然这两个人是不能上同一个老师的课的。

而且每个人可以上的课只有两种,我们记为 \(a_{i, 0 / 1}\)

假设 \(i\),\(j\) 对于当前的 \(mid\) 而言不能分在一起,其中 \(a_{i, 0} = a_{j, 0}\),那么我们可以发现:

  • \(i\) 上 \(a_{i, 0}\) 的课,则必须有 \(j\) 上 \(a_{j, 1}\) 的课
  • \(j\) 上 \(a_{j, 0}\) 的课,则必须有 \(i\) 上 \(a_{i, 1}\) 的课

可以发现这就是一个裸的 \(\text{2-SAT}\)

所以我们每次 \(\text{check}\) 都建图跑一遍 \(\text{2-SAT}\) 就好了。

参考代码:

#include <cstring>
#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
template < class T > inline T min(T a, T b) { return a < b ? a : b; }
template < class T > inline T max(T a, T b) { return a > b ? a : b; }
template < class T > inline void read(T& s) {
s = 0; int f = 0; char c = getchar();
while ('0' > c || c > '9') f |= c == '-', c = getchar();
while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
s = f ? -s : s;
} const int _ = 1010; int tot, head[_ * 3]; struct Edge { int v, nxt; } edge[_ * _ * 4];
inline void Add_edge(int u, int v) { edge[++tot] = (Edge) { v, head[u] }, head[u] = tot; } int n, t[_], a[_][2], f[_][_];
int num, dfn[_ * 3], low[_ * 3], col, co[_ * 3], top, stk[_ * 3]; inline void tarjan(int u) {
dfn[u] = low[u] = ++num, stk[++top] = u;
for (rg int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].v;
if (!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else
if (!co[v]) low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) { ++col;
do co[stk[top]] = col; while (stk[top--] != u);
}
} inline int id(int x, int y) { return y * (n + 1) + x; } inline void init() {
tot = num = col = top = 0;
memset(head, 0, sizeof head);
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
memset(co, 0, sizeof co);
} inline bool check(int mid) {
init();
for (rg int i = 1; i <= n; ++i)
for (rg int j = i + 1; j <= n; ++j) {
if (f[i][j] <= mid) continue ;
if (a[i][0] == a[j][0]) {
Add_edge(id(i, a[i][0]), id(j, a[j][1]));
Add_edge(id(j, a[j][0]), id(i, a[i][1]));
}
if (a[i][0] == a[j][1]) {
Add_edge(id(i, a[i][0]), id(j, a[j][0]));
Add_edge(id(j, a[j][1]), id(i, a[i][1]));
}
if (a[i][1] == a[j][0]) {
Add_edge(id(i, a[i][1]), id(j, a[j][1]));
Add_edge(id(j, a[j][0]), id(i, a[i][0]));
}
if (a[i][1] == a[j][1]) {
Add_edge(id(i, a[i][1]), id(j, a[j][0]));
Add_edge(id(j, a[j][1]), id(i, a[i][0]));
}
}
for (rg int i = 1; i <= n; ++i)
for (rg int k = 0; k < 3; ++k)
if (t[i] != k && !dfn[id(i, k)]) tarjan(id(i, k));
for (rg int i = 1; i <= n; ++i)
if (co[id(i, a[i][0])] == co[id(i, a[i][1])]) return 0;
return 1;
} int main() {
#ifndef ONLINE_JUDGE
file("cpp");
#endif
read(n);
for (rg int i = 1; i <= n; ++i) {
read(t[i]);
if (t[i] == 0) a[i][0] = 1, a[i][1] = 2;
if (t[i] == 1) a[i][0] = 0, a[i][1] = 2;
if (t[i] == 2) a[i][0] = 0, a[i][1] = 1;
for (rg int x, j = 1; j < n; ++j) read(x), f[i][x] = j;
}
for (rg int i = 1; i <= n; ++i)
for (rg int j = i + 1; j <= n; ++j) f[i][j] = max(f[i][j], f[j][i]);
int l = 1, r = n - 1;
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid)) r = mid; else l = mid + 1;
}
printf("%d\n", l);
return 0;
}

「JSOI2014」学生选课的更多相关文章

  1. 「JSOI2014」矩形并

    「JSOI2014」矩形并 传送门 我们首先考虑怎么算这个期望比较好. 我们不难发现每一个矩形要和 \(n - 1\) 个矩形去交,而总共又有 \(n\) 个矩形,所以我们把矩形两两之间的交全部加起来 ...

  2. 「JSOI2014」打兔子

    「JSOI2014」打兔子 传送门 首先要特判 \(k \ge \lceil \frac{n}{2} \rceil\) 的情况,因为此时显然可以消灭所有的兔子,也就是再环上隔一个点打一枪. 但是我们又 ...

  3. 「JSOI2014」电信网络

    「JSOI2014」电信网络 传送门 一个点选了就必须选若干个点,最大化点权之和,显然最大权闭合子图问题. 一个点向它范围内所有点连边,直接跑最大权闭合子图即可. 参考代码: #include < ...

  4. 「JSOI2014」歌剧表演

    「JSOI2014」歌剧表演 传送门 没想到吧我半夜切的 这道题应该算是 \(\text{JSOI2014}\) 里面比较简单的吧... 考虑用集合关系来表示分辨关系,具体地说就是我们把所有演员分成若 ...

  5. 「JSOI2014」支线剧情2

    「JSOI2014」支线剧情2 传送门 不难发现原图是一个以 \(1\) 为根的有根树,所以我们考虑树形 \(\text{DP}\). 设 \(f_i\) 表示暴力地走完以 \(i\) 为根的子树的最 ...

  6. 「JSOI2014」强连通图

    「JSOI2014」强连通图 传送门 第一问很显然就是最大的强连通分量的大小. 对于第二问,我们先把原图进行缩点,得到 \(\text{DAG}\) 后,统计出入度为零的点的个数和出度为零的点的个数, ...

  7. 「JSOI2014」序列维护

    「JSOI2014」序列维护 传送门 其实这题就是luogu的模板线段树2,之所以要发题解就是因为学到了一种比较NB的 \(\text{update}\) 的方式.(参见这题) 我们可以把修改操作统一 ...

  8. 「AHOI2014/JSOI2014」宅男计划

    「AHOI2014/JSOI2014」宅男计划 传送门 我们首先要发现一个性质:存货天数随买食物的次数的变化类似于单峰函数. 具体证明不会啊,好像是二分加三分来证明?但是没有找到明确的严格证明. 感性 ...

  9. 「AHOI2014/JSOI2014」拼图

    「AHOI2014/JSOI2014」拼图 传送门 看到 \(n \times m \le 10^5\) ,考虑根号分治. 对于 \(n < m\) 的情况,我们可以枚举最终矩形的上下边界 \( ...

随机推荐

  1. C++11 Lambda函数

    Lambda函数 C++11新增了lambda函数,其基本格式如下 [捕捉列表] (参数) mutable -> 返回值类型 {函数体} 说明 []是lambda的引出符,捕捉列表能够捕捉上下文 ...

  2. Blocked Billboard II

    前言 今天比赛真的状态不好(腐了一小会),导致差点爆0. 这个题解真的是在非常非常专注下写出来的,要不然真的心态崩. 刚换了域名,发现了美化脚本的bug,有点担心(汗-_-||). 题目 题目描述 奶 ...

  3. git 本地回滚与远程库回滚

    不说废话,开始: 一.本地回滚: git reset --hard commit-id //回滚到commit-id 二.远程回滚操作分3步:①将本地分支退回到某个commit    ②删除远程分支  ...

  4. QQ发起聊天

    QQ推广 网址: http://shang.qq.com/v3/widget.html 一键加群 实例: <a target="_blank" href="//sh ...

  5. vue 实现todolist,包含添加,删除,统计,清空,隐藏功能

    vue 实现todolist,包含添加,删除,统计,清空,隐藏功能 添加:生成列表结构(v-for+数组).获取用户输入(v-model).通过回车新增数据(v-on+.enter) 删除:点击删除指 ...

  6. XMOS发布集单片机,AI,FPGA,DSP于一身的跨界处理器完全体xcore.ai,致力于AIOT,售价1美元起步

    说明:XMOS这次致力于打造全新的,颠覆性的嵌入式平台,简化开发人员要学一堆东西才能开发一款高性能AIOT产品的痛点. XCORE.AI集单片机,AI,FPGA,DSP于一身,嵌入式软件开发人员可以灵 ...

  7. python中的坎坷之函数、集合

    函数 作用:实现一个功能,函数理解成一个工具,遇到问题把这个工具拿来用 优点:函数更方便,复用,可以在多个场景下用 1.带参数的函数 name='lzs-nice' nane.split('-') # ...

  8. [C/C++] _tprintf() 输出不了汉字

    在前面加一个 setlocale(LC_ALL, ""); //必须得有这行 否则不能输出中文 注意得加locale头文件 #include<stdio.h> #inc ...

  9. 吴裕雄 python 机器学习——模型选择数据集切分

    import numpy as np from sklearn.model_selection import train_test_split,KFold,StratifiedKFold,LeaveO ...

  10. 「JSOI2015」子集选取

    「JSOI2015」子集选取 传送门 看到这个数据范围,就知道肯定是要找规律. 如果把集合看成一个长度为 \(n\) 的 \(01\) 串, \(0\) 表示没有这个元素, \(1\) 表示有这个元素 ...