• P3825 [NOI2017]游戏


    题目背景

    狂野飙车是小 L 最喜欢的游戏。与其他业余玩家不同的是,小 L 在玩游戏之余,还精于研究游戏的设计,因此他有着与众不同的游戏策略。

    题目描述

    小 L 计划进行n场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。

    小 L 的赛车有三辆,分别用大写字母ABC表示。地图一共有四种,分别用小写字母xabc表示。其中,赛车A不适合在地图a上使用,赛车B不适合在地图b上使用,赛车C不适合在地图c上使用,而地图x则适合所有赛车参加。适合所有赛车参加的地图并不多见,最多只会有d张。

    n场游戏的地图可以用一个小写字母组成的字符串描述。例如:S=xaabxcbc表示小 L 计划进行8场游戏,其中第1场和第5场的地图类型是x,适合所有赛车,第2场和第3场的地图是a,不适合赛车A,第4场和第7场的地图是b,不适合赛车B,第6场和第8场的地图是c,不适合赛车C

    小 L 对游戏有一些特殊的要求,这些要求可以用四元组 (i,hi,j,hj)来描述,表示若在第i场使用型号为hi的车子,则第j场游戏要使用型号为hj的车子。

    你能帮小 L 选择每场游戏使用的赛车吗?如果有多种方案,输出任意一种方案。如果无解,输出 “-1’’(不含双引号)。

    输入格式

    输入第一行包含两个非负整数n,d。

    输入第二行为一个字符串S。n,d,S的含义见题目描述,其中S包含n个字符,且其中恰好d个为小写字母x。

    输入第三行为一个正整数m,表示有m条用车规则。接下来m行,每行包含一个四元组(i,h_i,j,h_j),其中i,j为整数,(h_i,h_j)为字符abc,含义见题目描述。

    输出格式

    输出一行。

    若无解输出 “-1’’(不含双引号)。

    若有解,则包含一个长度为nnn的仅包含大写字母ABC的字符串,表示小 L 在这nnn场游戏中如何安排赛车的使用。如果存在多组解,输出其中任意一组即可。

    因为spacial judge,最后一行不要输出回车。

    输入输出样例

    输入 #1

    3 1
    xcc
    1
    1 A 2 B
    

    输出 #1

    ABA
    

    输入 #2

    100 8
    bbccxabbcxaabbabcaaxbxaaccbcxcccbaccbccbbacaabcbcabxccbbabccabcabbacbbbbabccaabcaaacbabcacxabaxcabbb
    200
    61 B 14 A
    34 B 76 B
    17 B 13 A
    5 C 2 C
    90 A 73 C
    6 B 72 C
    21 A 1 C
    54 B 96 B
    2 C 44 B
    7 A 32 B
    71 A 83 C
    65 A 21 A
    32 B 45 B
    18 B 34 B
    51 A 13 A
    89 C 63 B
    26 B 22 C
    38 B 94 C
    86 C 95 C
    95 C 76 B
    67 B 100 A
    99 A 40 A
    35 A 53 B
    47 B 41 A
    36 B 69 A
    75 B 52 B
    90 A 7 A
    96 B 59 A
    92 C 98 C
    23 B 80 B
    13 A 48 A
    54 B 2 C
    93 C 39 A
    96 B 87 A
    66 A 15 C
    38 B 16 A
    54 B 41 A
    67 B 40 A
    45 B 66 A
    32 B 80 B
    34 B 59 A
    31 B 75 B
    65 A 78 C
    34 B 56 C
    28 B 8 A
    87 A 40 A
    56 C 40 A
    93 C 100 A
    31 B 41 A
    39 A 48 A
    55 C 28 B
    64 C 60 B
    69 A 82 C
    99 A 13 A
    47 B 30 B
    45 B 33 A
    88 A 75 B
    59 A 4 B
    53 B 44 B
    11 B 80 B
    52 B 50 C
    71 A 7 A
    41 A 63 B
    58 A 23 B
    55 C 96 B
    71 A 9 A
    24 B 34 B
    68 B 92 C
    15 C 18 B
    56 C 14 A
    98 C 39 A
    27 A 56 C
    95 C 31 B
    100 A 57 C
    62 C 51 A
    6 B 32 B
    9 A 99 A
    46 C 66 A
    21 A 1 C
    14 A 91 C
    61 B 37 A
    76 B 81 B
    80 B 43 B
    98 C 87 A
    56 C 49 A
    39 A 5 C
    88 A 65 A
    60 B 34 B
    95 C 11 B
    90 A 56 C
    61 B 48 A
    87 A 21 A
    46 C 84 A
    87 A 18 B
    36 B 52 B
    61 B 97 B
    40 A 36 B
    77 C 71 A
    78 C 26 B
    57 C 85 C
    75 B 95 C
    41 A 14 A
    59 A 1 C
    47 B 5 C
    11 B 88 A
    60 B 24 B
    35 A 98 C
    44 B 32 B
    2 C 69 A
    33 A 62 C
    65 A 72 C
    97 B 93 C
    94 C 27 A
    19 C 51 A
    63 B 45 B
    97 B 4 B
    10 A 8 A
    4 B 56 C
    12 C 66 A
    95 C 72 C
    41 A 30 B
    69 A 85 C
    13 A 2 C
    18 B 14 A
    71 A 3 B
    75 B 87 A
    26 B 24 B
    80 B 20 B
    2 C 50 C
    70 A 64 C
    46 C 18 B
    67 B 55 C
    25 B 22 C
    62 C 37 A
    40 A 56 C
    60 B 80 B
    37 A 28 B
    16 A 50 C
    82 C 34 B
    15 C 4 B
    88 A 42 B
    90 A 13 A
    17 B 21 A
    32 B 18 B
    22 C 53 B
    81 B 67 B
    71 A 48 A
    73 C 47 B
    21 A 34 B
    83 C 60 B
    40 A 78 C
    22 C 7 A
    79 C 55 C
    40 A 46 C
    58 A 79 C
    87 A 99 A
    92 C 55 C
    20 B 75 B
    72 C 41 A
    28 B 52 B
    60 B 50 C
    51 A 32 B
    96 B 24 B
    18 B 31 B
    83 C 59 A
    24 B 74 A
    88 A 97 B
    81 B 67 B
    51 A 72 C
    64 C 8 A
    51 A 50 C
    7 A 42 B
    4 B 78 C
    68 B 27 A
    70 A 95 C
    30 B 29 C
    96 B 81 B
    13 A 44 B
    4 B 82 C
    74 A 38 B
    92 C 49 A
    12 C 79 C
    46 C 44 B
    97 B 96 B
    15 C 60 B
    56 C 65 A
    61 B 14 A
    58 A 16 A
    43 B 26 B
    42 B 12 C
    72 C 24 B
    41 A 68 B
    4 B 5 C
    63 B 59 A
    86 C 88 A
    48 A 77 C
    36 B 59 A
    7 A 33 A
    2 C 56 C
    81 B 69 A
    

    输出 #2

    CABABCCABBCBCABAACBCCBCCAACBCBAACCBAAABCCCACCAACACCBAAAACCBACAABCCCACCCABCAABBAACBBACBCBBBCBABBACACA
    

    说明/提示

    【样例1解释】

    小 L 计划进行3场游戏,其中第1场的地图类型是x,适合所有赛车,第2场和第3场的地图是c,不适合赛车C

    小 L 希望:若第1场游戏使用赛车A,则第2场游戏使用赛车B。那么为这3场游戏分别安排赛车ABA可以满足所有条件。若依次为3场游戏安排赛车为BBBBAA时,也可以满足所有条件,也被视为正确答案。但依次安排赛车为AABABC时,因为不能满足所有条件,所以不被视为正确答案。

    img

    详细信息解释:
    
    1.score:QAQ:此测试点应该输出-1然而你输出的是别的
    
    2.score:QWQ:此测试点应该输出-1然而你输出的是-XXX(就是负号对了后面错了) 
    
    3.score:pwp:此测试点应该输出-1并且你的答案是正确的
    
    4.score:qwq:你的方案里出现了不是A,B,C的字符
    
    5.score:qaq:你的方案在第x的图中用了不让用的车
    
    6.score:pvp:你的方案不能满足第x个约束
    
    7.score:qvq:你的答案正确,恭喜嘤嘤嘤
    
    else:蛇皮judge没能正确读入应该读的东西……
    

    我好弱啊 , 不看题解根本想不到(看了题解也想不到) , 这个题要用2-SAT

    不考虑 x 的存在 , 那么一个位置适合的赛车就是只有两种 , 而 d <= 8 可以(2^8) 暴力大枚举,枚举 x 变成 a , 或者是 b(只用枚举这两种,因为选A 就是适合BC, 选B就是适合AC , 这样就等价于x这个位置可以填任意的字符);

    之后,考虑建图,

    分类讨论

    设 x , y 为位置 , px , py 是有限制的字符 , s 是总的字符串

    case 1 s[x] == px

    直接 continue

    因为这个条件是单向的 , 第一个都没法满足那这个条件就是废了。

    case 2 s[x] != px && s[y] == py

    这样说明 , 一个满足 , 一个不满足 , 这样 , x 位置就不能选择 px 这个字符 。

    于是就向x位置的另一个点连边。

    具体细节看代码。

    case 3 s[x] != px && s[y] != py

    这个就说明这个条件 , 可以成立 。

    将x向y对应的状态连边 , 再讲y的相反状态向x的相反状态连边 , 这个反向的边的意义是如果 y 不是 py 这个颜色,那x 也不能是 px 这个颜色 (因为 x 如果是px 这个颜色 , 那 y 一定是py 这个颜色)。

    zzcw(智障错误)

    1. 忘了建反向边。。。。。
    2. 由于 S 是小写 , 限制是大写 , 比较是没有转成一样在比较。
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N = 1e5+10;
    int n , d , m , top , cnt , tot , num;
    char s[N];
    int sta[N] , head[N] , vis[N] , col[N] , dfn[N] , low[N];
    struct opt{ int x , y; char c1 , c2; } q[N];
    struct edge{ int v , nex; } e[N<<1];
    void add(int u , int v) { e[++cnt].v = v; e[cnt].nex = head[u]; head[u] = cnt; return ; }
    void Read()
    {
    	scanf("%d%d", &n , &d); scanf("%s",s+1); scanf("%d",&m);
    	for(int i = 1 ; i <= m ; ++i) scanf("%d %c %d %c" , &q[i].x , &q[i].c1 , &q[i].y , &q[i].c2); // scanf() 真好用!!!
    	return ;
    }
    
    void Tarjan(int x)
    {
    	dfn[x] = low[x] = ++tot; vis[x] = 1; sta[++top] = x;
    	for(int i = head[x] , v; i ; i = e[i].nex)
    	{
    		v = e[i].v;
    		if(!dfn[v]) Tarjan(v) , low[x] = min(low[x] , low[v]);
    		else if(vis[v]) low[x] = min(low[x] , dfn[v]);
     	}
     	if(low[x] == dfn[x])
     	{
     		int t = sta[top]; num++; col[x] = num; vis[x] = 0;
    		while(t != x)
    		{
    			vis[t] = 0; col[t] = num;
    			t = sta[--top];
    		}
    		top--;
     	}
     	return ;
    }
    
    void Putans()
    {
    	for(int i = 1 ; i <= n ; ++i)
    	{
    		if(col[i] < col[i+n]) putchar(s[i] == 'a' ? 'B' : 'A');
    		else putchar(s[i] == 'c' ? 'B' : 'C');
    	}
    	return ;
    }
    
    bool solve()
    {
    	memset(head , 0 , sizeof head); cnt = 0;
    	memset(sta , 0 , sizeof sta); top = 0;
    	memset(dfn , 0 , sizeof dfn); tot = 0;
    	memset(low , 0 , sizeof low); num = 0;
    	memset(vis , 0 , sizeof vis);
    	memset(col , 0 , sizeof col);
    	register int i;
    	for(i = 1 ; i <= m ; ++i)
    	{       // wsxd !!!!!!!!!
    		if(s[q[i].x] - 32 == q[i].c1) continue; // 第一个就挂了
    		if(s[q[i].y] - 32 == q[i].c2) // 嗯。。。 不错还活着一个
    		{
    			// 这个的意思是
    			// 如果不能选的是 C 不适合的就是 A B 中的一个 , 这是 C 对应的就是后面的那个
    			// 否则就是不能选的是B 不适合的是C, 那可选的是 A,B(原来)B就是后面那个
    			if(q[i].c1 == 'C' || (q[i].c1 == 'B' && s[q[i].x] == 'c'))
    				add(q[i].x + n , q[i].x);
    			else 
    				add(q[i].x , q[i].x + n);
    		}
    		else // 两个都活着
    		{
    			// 要选都选 , 要不选都不选
    			// zzcw 居然没有建反向边
    			int opt1 = q[i].c1 == 'C' || (q[i].c1 == 'B' && s[q[i].x] == 'c');
    			int opt2 = q[i].c2 == 'C' || (q[i].c2 == 'B' && s[q[i].y] == 'c');
    			if(opt1 && opt2) add(q[i].x + n , q[i].y + n) , add(q[i].y , q[i].x);
    			else
    			if(opt1 && !opt2) add(q[i].x + n , q[i].y) , add(q[i].y + n , q[i].x);
    			else
    			if(!opt1 && opt2) add(q[i].x , q[i].y + n) , add(q[i].y , q[i].x + n);
    			else
    			if(!opt1 && !opt2) add(q[i].x , q[i].y) , add(q[i].y + n , q[i].x + n);
    		}
    	}
    	for(int i = 1 ; i <= (n << 1) ; ++i) if(!dfn[i]) Tarjan(i);
    	for(int i = 1 ; i <= n ; ++i) if(col[i] == col[i+n]) return false;
    	return true;
    }
    
    int main()
    {
    	Read();
    	d = 0; int pos[10] = {0};
    	register int i , j;
    	for(i = 1 ; i <= n ; ++i) if(s[i] == 'x') pos[++d] = i;
    	for(i = 0 ; i <= (1 << d) - 1 ; ++i)
    	{
    		for(j = 1 ; j <= d ; ++j) s[pos[j]] = i & (1 << (j - 1)) ? 'a' : 'b';
    		if(solve()) { Putans(); return 0; }
    	}
    	printf("-1");
    	return 0;
    }
    
  • 相关阅读:
    【组合数学】AGC036C
    【数位贪心】loj#530. 「LibreOJ β Round #5」最小倍数
    【概率dp】vijos 3747 随机图
    【线段树 经典技巧】10.7序列绝对值
    【杂题】10.7爬树
    【组合数学 思维题】10.6种树
    【换根dp】9.22小偷
    【高维前缀和】8.15B. 组合数
    【技巧 dp】1566: [NOI2009]管道取珠
    【经典dp 技巧】8.13序列
  • 原文地址:https://www.cnblogs.com/R-Q-R-Q/p/12177638.html
Copyright © 2020-2023  润新知