• 题解 P3825 【[NOI2017]游戏】


    貌似这道题挺妙的样子?(其实是我太弱了)

    也算是一篇(2-sat)的小结吧?

    (2-sat)

    给定一些布尔类型,每个点只有(0/1)两种取值,然后给出多种限制条件,要求方案。

    考虑把每个点拆成两个点,如:(a->a_1,a_0)

    然后对于每种限制条件进行不同的连边。

    以这道题为例,

    我们先考虑(d = 0)的条件。

    那么注意到每个点它只有两个取值,我们可以先预处理出来。

    然后这就是一个(2-sat)问题。

    考虑连边条件。

    如果一个条件是(x)(A),则(y)(B)

    但是(x)位置上的点就是(a),那么说明这种条件是无法成立的,我们忽略。

    如果(x)位置上不是(a)(比如是(B)),但(y)(b),那么就有:若(x)(A),则(y)(B),但(y)不能为(B),所以(x)亦不能为(A),我们就把(x_A)连向(x_C)说明(x_A)不能取。

    其他条件的话需要考虑周全,大概是这样连:

    (x_A->y_B)

    (y_{other} -> x_{other})(如果(y)不为(B),那么(x)亦不为(A)

    这样我们就能处理(d = 0)的情况了

    如果(dquad!=quad0)

    因为(d)那么小,所以暴枚啊!

    我们可以枚举(x)这个位置不能取那个点,这样枚举的话,如果(x)不能取(A),我们就把(B,C)存在的答案包括了进去,如果枚举(x)不能取(B),那么就把(A,C)的答案包括了进去。

    所以对于每个(x)我们只要枚举(2)次。

    复杂度(O(2^d*N))

    #include<bits/stdc++.h>
    using namespace std;
    #define re register
    #define rep( i, s, t ) for( re int i = s; i <= t; ++ i ) 
    #define Next( i, x ) for( re int i = head[x]; i; i = e[i].next ) 
    int read() {
    	char cc = getchar(); int cn = 0, flus = 1;
    	while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
    	while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
    	return cn * flus;
    }
    
    const int N = 5e5 + 5;
    int head[N], dfn[N], low[N], s[N], cnt, n, m, top, tim, col, color[N];
    int stac[N], top2, d, suit[N], ch[N][2];
    struct E {
    	int to, next;
    }e[N * 2];
    struct Node{
    	int x, hx, y, hy;
    }q[N];
    char S[N];
    
    void add( int x, int y ) {
    	e[++cnt] = (E) { y, head[x] }, head[x] = cnt;
    } 
    void trajan( int x ) {
    	dfn[x] = low[x] = ++tim, s[++ top] = x;
    	Next( i, x ) {
    		int v = e[i].to;
    		if( !dfn[v] )  trajan( v ), low[x] = min( low[x], low[v] );
    		else if( !color[v] )  low[x] = min( low[x], dfn[v] );
    	}
    	if( low[x] == dfn[x] ) {
    		++ col;
    		while( s[top + 1] != x ) color[s[top--]] = col;
    	}
    } 
    
    void doit() {
    	memset( head, 0, sizeof(head) ), memset( dfn, 0, sizeof(dfn) );
    	memset( low, 0, sizeof(low) ), memset( color, 0, sizeof(color) );
    	cnt = tim = top = col = 0;
    	rep( i, 1, m ) {
    		int x = q[i].x, y = q[i].y, u = x + q[i].hx * n, v = y + q[i].hy * n;
    		if( q[i].hx == suit[x] ) continue;
    		if( q[i].hy == suit[y] ) {
    			add( ch[x][ch[x][1] == u], ch[x][ch[x][1] != u] ); 
    		}
    		else add( u, v ), add( ch[y][ch[y][1] != v], ch[x][ch[x][1] != u]);
    	}
    	rep( i, 1, n ) {  if( !dfn[ch[i][0]] ) trajan( ch[i][0] );  if( !dfn[ch[i][1]] ) trajan( ch[i][1] );  }
    	rep( i, 1, n ) if( color[ch[i][0]] == color[ch[i][1]] ) return ;
    	rep(i, 1, n) {
    		if( color[ch[i][0]] > color[ch[i][1]] ) printf("%c", ( ( ch[i][1] - 1 ) / n ) + 'A'  );
    		else printf("%c", ( ( ch[i][0] - 1 ) / n ) + 'A' );
    	}
        exit(0);
    }
    void dfs( int x ) {
    	if( x > d ) {
    		doit();
    		return ;
    	}
    	int now = stac[x];
    	suit[now] = 0, ch[now][0] = now + n, ch[now][1] = now + 2 * n,
    	dfs( x + 1 );
    	suit[now] = 1, ch[now][0] = now, ch[now][1] = now + 2 * n;
    	dfs( x + 1 );
    }
    signed main()
    {
    	n = read(), d = read();
    	scanf( "%s", S );
    	int len = strlen(S);
    	rep( i, 0, len - 1 ) {
    		int now = i + 1;
    		if( S[i] == 'x' ) stac[++top2] = now;
    		if( S[i] == 'a' ) suit[now] = 0, ch[now][0] = now + n, ch[now][1] = now + 2 * n;
    		if( S[i] == 'b' ) suit[now] = 1, ch[now][0] = now, ch[now][1] = now + 2 * n;
    		if( S[i] == 'c' ) suit[now] = 2, ch[now][0] = now, ch[now][1] = now + n; 
    	}
    	m = read();
    	rep( i, 1, m )
    		q[i].x = read(), q[i].hx = getchar() - 'A', q[i].y = read(), q[i].hy = getchar() - 'A';
    	dfs( 1 );
    	printf("-1");
    	return 0;
    } 
    
  • 相关阅读:
    flash
    应用缓存
    音频和视频
    拖拽借口
    地理定位接口
    表单元素
    jquery常见用法
    jquery ajax 模板
    Ubuntu下Lucene环境搭配
    小絮叨
  • 原文地址:https://www.cnblogs.com/Soulist/p/10630340.html
Copyright © 2020-2023  润新知