[题解] [JSOI2014] 学生选课

题面

题解

容易想到二分, 问题是如何 check

发现对于一个点, 他能够选的只有两种, 对于他连的点, 能够选的也只有两种

又因为总共只有三种方案, 那么一条边连的两个点最少会有一种一样的

那么这个选了这种, 那个就不能选这种, 那个选了这种, 这个就选不了这种

嗯, 是个 2 - sat

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
const int N = 1005; 
using namespace std; 

int n, a[N], f[N][N], head[N << 1], dfn[N << 1], low[N << 1], stk[N << 1], top, instk[N << 1], cnt, tot, bl[N << 1], id[N][2], num[N][2], ans;
struct edge { int to, nxt; } e[N * N * 2]; 

template < typename T >
inline T read()
{
    T x = 0, w = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * w; 
}

inline void adde(int u, int v) { e[++cnt] = (edge) { v, head[u] }, head[u] = cnt; }

void tarjan(int u)
{
    dfn[u] = low[u] = ++cnt, instk[stk[++top] = u] = 1;
    for(int v, i = head[u]; i; i = e[i].nxt)
    {
	v = e[i].to;
	if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
	else if(instk[v]) low[u] = min(low[u], dfn[v]); 
    }
    if(low[u] >= dfn[u])
    {
	tot++; int x;
	do
	{
	    instk[x = stk[top--]] = 0;
	    bl[x] = tot; 
	}
	while(x != u); 
    }
}

void clear()
{
    memset(dfn, 0, sizeof(dfn)), memset(low, 0, sizeof(low));
    memset(head, 0, sizeof(head)), cnt = tot = 0; 
}

bool check(int mid)
{
    clear();
    for(int x, y, i = 1; i <= n; i++)
	for(int j = i + 1; j <= n; j++)
	    if(f[i][j] > mid)
	    {
		if(id[i][0] == id[j][0] || id[i][0] == id[j][1])
		    x = 0, y = id[i][0] == id[j][1]; 
		else x = 1, y = id[i][1] == id[j][1]; 
		adde(num[i][x], num[j][y ^ 1]), adde(num[j][y], num[i][x ^ 1]); 
		if(id[i][x ^ 1] == id[j][y ^ 1])
		    adde(num[i][x ^ 1], num[j][y]), adde(num[j][y ^ 1], num[i][x]); 
	    }
    cnt = 0;
    for(int i = 1; i <= 2 * n; i++)
	if(!dfn[i]) tarjan(i); 
    for(int i = 1; i <= n; i++)
	if(bl[num[i][0]] == bl[num[i][1]]) return 0;
    return 1; 
}

int main()
{
    n = read <int> (); 
    for(int i = 1; i <= n; i++)
    {
	a[i] = read <int> (), num[i][1] = i, num[i][0] = i + n; 
	id[i][0] = (!a[i] ? 1 : 0), id[i][1] = (a[i] == 2 ? 1 : 2); 
	for(int x, j = 1; j < n; j++)
	    x = read <int> (), f[i][x] = j; 
    }
    for(int i = 1; i <= n; i++)
	for(int j = i + 1; j <= n; j++)
	    f[i][j] = max(f[i][j], f[j][i]); 
    int l = 1, r = n; 
    while(l <= r)
    {
	int mid = (l + r) >> 1;
	if(check(mid)) ans = mid, r = mid - 1;
	else l = mid + 1; 
    }
    printf("%d
", ans); 
    return 0; 
}
原文地址:https://www.cnblogs.com/ztlztl/p/12296709.html