• [题解] [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; 
    }
    
  • 相关阅读:
    linux根目录空间不足
    兴趣点 / 关键点( Interest point/Keypoint )
    opencv批量修改图片尺寸
    Excel批量修改文件
    xm数据写入
    opencv矩阵操作
    SVM参数解析
    Mat取行或列
    clone()与image和 cloneTo()
    最大连通域(指针)
  • 原文地址:https://www.cnblogs.com/ztlztl/p/12296709.html
Copyright © 2020-2023  润新知