Solution
这是NOI 2017 Day2的T1。
这是一道签到题(然而好像卡dfs)。。
这题貌似是本学校的一位校友(大神)出的。。。
一开始看到这题以为是3-SAT,NPC问题啊,怎么做?后来在机房同学们的讨论下(网络同步赛大家都集中起来),发现其实只是一道水水的2-SAT。首先,看到输出任意解的题目就可以想2-SAT。求2-SAT是可以达到线性的复杂度的,题目的关键在于d<=8,即给出的字符串中x的状态集合就是3^8那么多种。那么我们直接暴力枚举就可以了,我们只用枚举2^8=256种就行了,因为假设x取a,x取b,分别代表该地图能用的车为B,C和A,C。这就已经包含全部情况了。暴力枚举过后直接上2-SAT。
具体怎么实现呢?就是根据题目要求连边,若A则B,就从A向B连一条边,同时从!B向!A连一条边(原命题与逆否命题同真或同假)。由于有了地图限制,所以每组最多只有两种选择,这就是2-SAT了。不过与一般的题目不同的是每一组的事件A和事件!A所代表的不尽相同而已。最后直接根据选取的合法方案输出一组,然后结束暴力枚举。或者枚举结束仍无答案输出-1。连边时需要注意的是:
①如果限制为若A则B,且A不能选就直接跳过,不用连边。因为此时B和!B可以选,而输出时会根据地图,所以不会选A。
②如果限制为若A则B,且B不能选,则A也一定不能选,所以我们将A连向!A就可以了。
讲了这么多。这里实现2-SAT如果用dfs,回退标记什么的会T掉最后三个点,因为dfs的时间是不能保证的,而此题卡常数。所以一开始我是过不了的。后来我改成用Tarjan求scc就能过了,因为这样做一定是线性的,而且,不管了,反正这样能过,大概是数据太大,选取的实现方法变得关键了吧。。
Code(dfs TLE)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#define N 50010
#define M 100010
using namespace std;
int n, d, m;
int back[N], head_p[N<<1], cur;
char x[5], y[5], S[N];
bool vis[N<<1];
struct Adj{int next, obj;} Edg[M<<1];
struct Da{
int l, r, x, y;
}q[M];
void Add(int a, int b){
cur ++;
Edg[cur].next = head_p[a];
Edg[cur].obj = b;
head_p[a] = cur;
}
bool Dfs(int now){
if(now <= n && vis[now+n] || now > n && vis[now-n]) return false;
if(vis[now]) return true;
vis[now] = true;
back[++back[0]] = now;
for(register int i = head_p[now]; ~ i; i = Edg[i].next){
int v = Edg[i].obj;
if(!Dfs(v)) return false;
}
return true;
}
bool Sat(){
memset(vis, false, sizeof(vis));
for(register int i = 1; i <= n; i++){
if(vis[i] || vis[i+n]) continue;
back[0] = 0;
if(!Dfs(i)){
for(register int j = 1; j <= back[0]; j++) vis[back[j]] = false;
back[0] = 0;
if(!Dfs(i+n)) return false;
}
}
return true;
}
bool Work(){
memset(head_p, -1, sizeof(head_p));
cur = -1;
for(register int i = 1; i <= m; i++){
int u, v;
if(S[q[i].l-1] == 'a' && q[i].x == 1) continue;
if(S[q[i].l-1] == 'b' && q[i].x == 2) continue;
if(S[q[i].l-1] == 'c' && q[i].x == 3) continue;
if(S[q[i].l-1] == 'a' && q[i].x == 2) u = q[i].l;
if(S[q[i].l-1] == 'a' && q[i].x == 3) u = q[i].l+n;
if(S[q[i].l-1] == 'b' && q[i].x == 1) u = q[i].l;
if(S[q[i].l-1] == 'b' && q[i].x == 3) u = q[i].l+n;
if(S[q[i].l-1] == 'c' && q[i].x == 1) u = q[i].l;
if(S[q[i].l-1] == 'c' && q[i].x == 2) u = q[i].l+n;
if(S[q[i].r-1] == 'a' && q[i].y == 2) v = q[i].r;
if(S[q[i].r-1] == 'a' && q[i].y == 3) v = q[i].r+n;
if(S[q[i].r-1] == 'b' && q[i].y == 1) v = q[i].r;
if(S[q[i].r-1] == 'b' && q[i].y == 3) v = q[i].r+n;
if(S[q[i].r-1] == 'c' && q[i].y == 1) v = q[i].r;
if(S[q[i].r-1] == 'c' && q[i].y == 2) v = q[i].r+n;
if(S[q[i].r-1] == 'a' && q[i].y == 1) v = (u <= n) ? u + n : u - n;
if(S[q[i].r-1] == 'b' && q[i].y == 2) v = (u <= n) ? u + n : u - n;
if(S[q[i].r-1] == 'c' && q[i].y == 3) v = (u <= n) ? u + n : u - n;
Add(u, v);
u <= n ? u += n : u -= n;
v <= n ? v += n : v -= n;
Add(v, u);
}
return Sat();
}
void Fill(int now){
if(now == n+1){
if(Work()){
for(register int i = 1; i <= n; i++){
if(S[i-1] == 'a' && vis[i]) putchar('B');
if(S[i-1] == 'b' && vis[i]) putchar('A');
if(S[i-1] == 'c' && vis[i]) putchar('A');
if(S[i-1] == 'a' && vis[i+n]) putchar('C');
if(S[i-1] == 'b' && vis[i+n]) putchar('C');
if(S[i-1] == 'c' && vis[i+n]) putchar('B');
}
exit(0);
}
return;
}
if(S[now-1] == 'x'){
S[now-1] = 'a';
Fill(now+1);
S[now-1] = 'b';
Fill(now+1);
}
else Fill(now+1);
}
int main(){
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
scanf("%d%d%s%d", &n, &d, &S, &m);
for(register int i = 1; i <= m; i++){
scanf("%d%s%d%s", &q[i].l, &x, &q[i].r, &y);
if(x[0] == 'A') q[i].x = 1;
else if(x[0] == 'B') q[i].x = 2;
else q[i].x = 3;
if(y[0] == 'A') q[i].y = 1;
else if(y[0] == 'B') q[i].y = 2;
else q[i].y = 3;
}
Fill(1);
putchar('-');
putchar('1');
return 0;
}
Code(Tarjan求scc AC)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
#define N 50010
#define M 100010
using namespace std;
int n, d, m;
int head_p[N<<1], cur, cur1, head_p1[N<<1];
char x[5], y[5], S[N];
struct Adj{int next, obj;} Edg[M<<1], Edg1[M<<1];
struct Da{
int l, r, x, y;
}q[M];
int low[N<<1], stack[N<<1], Fa[N<<1], state[N<<1], Root[N<<1], in[N<<1], num[N<<1], dfn[N<<1], Tim, top;
bool vis[N<<1];
queue <int> Q;
void Add(int a, int b){
cur ++;
Edg[cur].next = head_p[a];
Edg[cur].obj = b;
head_p[a] = cur;
}
void Insert(int a, int b){
cur1 ++;
Edg1[cur1].next = head_p1[a];
Edg1[cur1].obj = b;
head_p1[a] = cur1;
}
void Tarjan(int root){
low[root] = dfn[root] = ++Tim;
state[root] = 1;
stack[++top] = root;
for(int i = head_p[root]; ~ i; i = Edg[i].next){
int v = Edg[i].obj;
if(state[v] == 2) continue;
if(!state[v]) Tarjan(v);
low[root] = min(low[root], low[v]);
}
if(low[root] == dfn[root]){
Root[++Root[0]] = root;
while(top){
int tmp = stack[top--];
state[tmp] = 2;
Fa[tmp] = root;
if(tmp == root) break;
}
}
}
void Scc(){
memset(state, 0, sizeof(state));
Tim = top = Root[0] = 0;
for(int i = 1; i <= (n<<1); i++)
if(!state[i]) Tarjan(i);
cur1 = -1;
memset(head_p1, -1, sizeof(head_p1));
for(int i = 1; i <= (n<<1); i++)
for(int j = head_p[i]; ~ j; j = Edg[j].next){
int v = Edg[j].obj;
if(Fa[v] != Fa[i]){
Insert(Fa[i], Fa[v]);
in[Fa[v]] ++;
}
}
int y = 0;
memset(num, 0, sizeof(num));
for(int i = 1; i <= Root[0]; i++) if(!in[Root[i]]) num[Root[i]] = ++y, Q.push(Root[i]);
while(!Q.empty()){
int now = Q.front();
Q.pop();
for(int i = head_p1[now]; ~ i; i = Edg1[i].next){
int v = Edg1[i].obj;
in[v] --;
if(!in[v]) num[v] = ++y, Q.push(v);
}
}
}
bool Sat(){
Scc();
memset(vis, false, sizeof(vis));
for(int i = 1; i <= n; i++){
if(num[Fa[i]] == num[Fa[i+n]]) return false;
if(num[Fa[i]] > num[Fa[i+n]]) vis[i] = true;
else vis[i+n] = true;
}
return true;
}
bool Work(){
memset(head_p, -1, sizeof(head_p));
cur = -1;
for(register int i = 1; i <= m; i++){
int u, v;
if(S[q[i].l-1] == 'a' && q[i].x == 1) continue;
if(S[q[i].l-1] == 'b' && q[i].x == 2) continue;
if(S[q[i].l-1] == 'c' && q[i].x == 3) continue;
if(S[q[i].l-1] == 'a' && q[i].x == 2) u = q[i].l;
if(S[q[i].l-1] == 'a' && q[i].x == 3) u = q[i].l+n;
if(S[q[i].l-1] == 'b' && q[i].x == 1) u = q[i].l;
if(S[q[i].l-1] == 'b' && q[i].x == 3) u = q[i].l+n;
if(S[q[i].l-1] == 'c' && q[i].x == 1) u = q[i].l;
if(S[q[i].l-1] == 'c' && q[i].x == 2) u = q[i].l+n;
if(S[q[i].r-1] == 'a' && q[i].y == 2) v = q[i].r;
if(S[q[i].r-1] == 'a' && q[i].y == 3) v = q[i].r+n;
if(S[q[i].r-1] == 'b' && q[i].y == 1) v = q[i].r;
if(S[q[i].r-1] == 'b' && q[i].y == 3) v = q[i].r+n;
if(S[q[i].r-1] == 'c' && q[i].y == 1) v = q[i].r;
if(S[q[i].r-1] == 'c' && q[i].y == 2) v = q[i].r+n;
if(S[q[i].r-1] == 'a' && q[i].y == 1) v = (u <= n) ? u + n : u - n;
if(S[q[i].r-1] == 'b' && q[i].y == 2) v = (u <= n) ? u + n : u - n;
if(S[q[i].r-1] == 'c' && q[i].y == 3) v = (u <= n) ? u + n : u - n;
Add(u, v);
u <= n ? u += n : u -= n;
v <= n ? v += n : v -= n;
Add(v, u);
}
return Sat();
}
void Fill(int now){
if(now == n+1){
if(Work()){
for(register int i = 1; i <= n; i++){
if(S[i-1] == 'a' && vis[i]) putchar('B');
if(S[i-1] == 'b' && vis[i]) putchar('A');
if(S[i-1] == 'c' && vis[i]) putchar('A');
if(S[i-1] == 'a' && vis[i+n]) putchar('C');
if(S[i-1] == 'b' && vis[i+n]) putchar('C');
if(S[i-1] == 'c' && vis[i+n]) putchar('B');
}
exit(0);
}
return;
}
if(S[now-1] == 'x'){
S[now-1] = 'a';
Fill(now+1);
S[now-1] = 'b';
Fill(now+1);
}
else Fill(now+1);
}
int main(){
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
scanf("%d%d%s%d", &n, &d, &S, &m);
for(register int i = 1; i <= m; i++){
scanf("%d%s%d%s", &q[i].l, &x, &q[i].r, &y);
if(x[0] == 'A') q[i].x = 1;
else if(x[0] == 'B') q[i].x = 2;
else q[i].x = 3;
if(y[0] == 'A') q[i].y = 1;
else if(y[0] == 'B') q[i].y = 2;
else q[i].y = 3;
}
Fill(1);
putchar('-');
putchar('1');
return 0;
}
樱花满地集于我心,楪舞纷飞祈愿相随。