题意
小 L 计划进行 n n 场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。
小 L 的赛车有三辆,分别用大写字母 A、B、C 表示。地图一共有四种,分别用小写字母 x、a、b、c 表示。其中,赛车 A 不适合在地图 a 上使用,赛车 B 不适合在地图 b 上使用,赛车 C 不适合在地图 c 上使用,而地图 x 则适合所有赛车参加。适合所有赛车参加的地图并不多见,最多只会有 d d 张。
n n 场游戏的地图可以用一个小写字母组成的字符串描述。例如:S = x a a b x c b c – ––––––––––––– – S = x a a b x c b c _ 表示小 L 计划进行 8 场游戏,其中第 1 场和第 5 场的地图类型是 x,适合所有赛车,第 2 场和第 3 场的地图是 a,不适合赛车 A,第 4 场和第 7 场的地图是 b,不适合赛车 B,第 6 场和第 8 场的地图是 c,不适合赛车 C。
小 L 对游戏有一些特殊的要求(共 m m 条),这些要求可以用四元组 ( i , h i , j , h j ) ( i , h i , j , h j ) 来描述,表示若在第 i i 场使用型号为 h i h i 的车子,则第 j j 场游戏要使用型号为 h j h j 的车子。
你能帮小 L 选择每场游戏使用的赛车吗?如果有多种方案,输出任意一种方案。如果无解,输出 “-1”(不含双引号)。
数据范围
n ≤ 50000 , d ≤ 8 , m ≤ 100000. n ≤ 50000 , d ≤ 8 , m ≤ 100000.
题解
话说我NOI的题改起来好费力啊TAT 一道就一天(还对着数据改)
不难发现这和之前一道题类似LA 3713 。
虽然有很多种类,但每个人也只有两种可以选择,并且关系也是二元的。
所以我们就可以套2-SAT模板了qwq 不会2-SAT请点这里 .
首先暴力枚举每个 x x 的状态,从 a , b , c a , b , c 中选取一个就行了。
然后对于那些二元关系 ( i , h i , j , h j ) ( i , h i , j , h j ) ,就如下考虑。
i i 可以选择 h i h i 这种赛车
j j 无法选择 h j h j 这种赛车,那么 i i 就必不能选 h i h i 了,就连一条 h i → ¬ h i h i → ¬ h i 的边(此处 ¬ h i ¬ h i 表示 i i 可以选择的另一种赛车)
j j 可以选择 h j h j 这种赛车,那么就只要连 h i → h j h i → h j 的边就好了
i i 不可以选择 h i h i 这种赛车:那就直接跳过就行了。
然后记得要建逆否命题等价 的另一条边。
如果用DFS 复杂度为 Θ ( 3 d n m ) Θ ( 3 d n m ) ,Tarjan 就是 Θ ( 3 d ( n + m ) ) Θ ( 3 d ( n + m ) ) 啦。
其实第二个Tarjan 复杂度已经足够过了。。。但是能更优!
有这样一个结论:
如果一个 x x 已经考虑过 a , b a , b 两个状态了,那么我们就不用考虑为 c c 的状态。
这是为什么呢?
因为 ( A , B ) ( A , B ) 和 ( B , C ) ( B , C ) 的两种选择你都已经考虑过了,那么剩下一种 ( A , C ) ( A , C ) 你前面一定会在前面一种选过。(因为每个点只能从那三种中选择一种赛车,如果另外两种都不可行,剩下一种也不行了)
这样我们就可以将复杂度降为 Θ ( 2 d ( n + m ) ) Θ ( 2 d ( n + m ) ) qwq
然而又有垃圾UOJ hack数据 我判了一下快到0.8s的时候,直接输出"-1"...才过 (卡常数真的恶心 )
代码
我一开始写了个DFS 的可以跑过85分.... 然而有人能用这个A掉 Orz
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
inline bool chkmin (int &a, int b) {return b < a ? a = b, 1 : 0 ;}
inline bool chkmax (int &a, int b) {return b > a ? a = b, 1 : 0 ;}
inline int read () {
int x = 0 , fh = 1 ; char ch = getchar ();
for (; !isdigit (ch); ch = getchar ()) if (ch == '-' ) fh = -1 ;
for (; isdigit (ch); ch = getchar ()) x = (x * 10 ) + (ch ^ 48 );
return x * fh;
}
void File () {
#ifdef zjp_shadow
freopen ("317.in" , "r" , stdin);
freopen ("317.out" , "w" , stdout);
#endif
}
const int N = 100010 ;
char str[N];
int Jump[N], len, Last;
int m;
struct Limit { int u, v; char colu, colv; } lt[N];
char opt[N][2 ];
struct Two_SAT {
bitset<N> mark;
int Next[N], Head[N], to[N], e, n;
void Init (int n) { this -> n = n; mark.reset (); Set (Head, 0 ); e = 0 ; }
void add_edge (int u, int v) { to[++ e] = v; Next[e] = Head[u]; Head[u] = e; }
void Add (int x, int xv, int y, int yv) {
x = x << 1 | xv; y = y << 1 | yv;
add_edge (x, y); add_edge (y ^ 1 , x ^ 1 );
}
int sta[N], top;
bool Dfs (int x) {
if (mark[x ^ 1 ]) return false ;
if (mark[x]) return true ;
sta[++ top] = x; mark[x] = true ;
for (int i = Head[x]; i; i = Next[i])
if (!Dfs (to[i])) return false ;
return true ;
}
bool Solve () {
for (int i = 2 ; i <= 2 * n; i += 2 )
if (!mark[i] && !mark[i ^ 1 ]) {
top = 0 ;
if (!Dfs (i)) {
while (top) mark[sta[top --]] = false ;
if (!Dfs (i ^ 1 )) { return false ; }
}
}
return true ;
}
} T;
int n;
inline bool Check () {
T.Init (n);
For (i, 1 , m) {
int u = lt[i].u, v = lt[i].v, colu = lt[i].colu, colv = lt[i].colv;
if (u == v && colu == colv) continue ;
For (v1, 0 , 1 ) if (opt[u][v1] == colu) {
bool flag = false ;
For (v2, 0 , 1 ) if (opt[v][v2] == colv) { flag = true ; T.Add (u, v1, v, v2); }
if (!flag)
T.Add (u, v1, u, v1 ^ 1 );
}
}
return T.Solve ();
}
inline void Out () {
For (i, 1 , n) For (j, 0 , 1 ) if (T.mark[i << 1 | j]) putchar (toupper (opt[i][j]));
}
void Dfs (int pos) {
if (pos == len + 1 ) { if (Check ()) { Out (); exit (0 ); } return ; }
opt[pos][0 ] = 'b' ; opt[pos][1 ] = 'c' ; str[pos] = 'a' ; Dfs (Jump[pos]);
opt[pos][0 ] = 'a' ; opt[pos][1 ] = 'c' ; str[pos] = 'b' ; Dfs (Jump[pos]);
}
int main () {
File ();
n = len = read (); read ();
scanf ("%s" , str + 1 );
Last = len + 1 ;
Fordown (i, len, 1 )
if (str[i] == 'x' ) { Jump[i] = Last; Last = i; }
else { int cnt = 0 ; For (j, 0 , 2 ) if (str[i] != 'a' + j) { opt[i][cnt ++] = 'a' + j; } }
m = read ();
For (i, 1 , m) {
int u, v; char colu, colv;
scanf ("%d %c %d %c" , &u, &colu, &v, &colv);
lt[i].u = u; lt[i].v = v; lt[i].colu = tolower (colu); lt[i].colv = tolower (colv);
}
Dfs (Last);
puts ("-1" );
return 0 ;
}
正解就用Tarjan 缩点就行咯qwq
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
inline bool chkmin (int &a, int b) {return b < a ? a = b, 1 : 0 ;}
inline bool chkmax (int &a, int b) {return b > a ? a = b, 1 : 0 ;}
inline int read () {
int x = 0 , fh = 1 ; char ch = getchar ();
for (; !isdigit (ch); ch = getchar ()) if (ch == '-' ) fh = -1 ;
for (; isdigit (ch); ch = getchar ()) x = (x * 10 ) + (ch ^ 48 );
return x * fh;
}
void File () {
#ifdef zjp_shadow
freopen ("317.in" , "r" , stdin);
freopen ("317.out" , "w" , stdout);
#endif
}
const int N = 200100 ;
char str[N];
int Jump[N], len, Last;
int m;
struct Limit { int u, v; char colu, colv; } lt[N];
char opt[N][2 ];
struct Two_SAT {
int Next[N], Head[N], to[N], e, n;
void Init (int n) { this -> n = n; For (i, 2 , n << 1 | 1 ) Head[i] = 0 ; e = 0 ; }
void add_edge (int u, int v) { to[++ e] = v; Next[e] = Head[u]; Head[u] = e; }
void Add (int x, int xv, int y, int yv) {
x = x << 1 | xv; y = y << 1 | yv;
add_edge (x, y); add_edge (y ^ 1 , x ^ 1 );
}
int sccno[N], scc_cnt;
int dfn[N], lowlink[N];
int sta[N], top, clk;
void Tarjan (int u) {
lowlink[u] = dfn[u] = ++clk; sta[++ top] = u;
for (int i = Head[u]; i; i = Next[i]) {
int v = to[i];
if (!dfn[v]) { Tarjan (v); chkmin (lowlink[u], lowlink[v]); }
else if (!sccno[v]) chkmin (lowlink[u], dfn[v]);
}
if (lowlink[u] == dfn[u]) {
++ scc_cnt; for (;;) { int now = sta[top --]; sccno[now] = scc_cnt; if (now == u) break ; }
}
}
bool Solve () {
For (i, 2 , n << 1 | 1 ) dfn[i] = sccno[i] = 0 ; scc_cnt = clk = 0 ;
For (i, 2 , n << 1 | 1 ) if (!dfn[i]) Tarjan (i);
For (i, 1 , n)
if (sccno[i << 1 ] == sccno[i << 1 | 1 ]) return false ;
return true ;
}
void Out () {
For (i, 1 , n) { putchar (toupper (opt[i][sccno[i << 1 ] > sccno[i << 1 | 1 ]])) ; }
}
} T;
int n;
int cnt = 0 ;
inline bool Check () {
T.Init (n);
For (i, 1 , m) {
int u = lt[i].u, v = lt[i].v, colu = lt[i].colu, colv = lt[i].colv;
if (u == v && colu == colv) continue ;
For (v1, 0 , 1 ) if (opt[u][v1] == colu) {
bool flag = false ;
For (v2, 0 , 1 ) if (opt[v][v2] == colv) { flag = true ; T.Add (u, v1, v, v2); }
if (!flag) T.Add (u, v1, u, v1 ^ 1 );
}
}
int res = T.Solve ();
return res;
}
void Dfs (int pos) {
if ((double ) clock () / CLOCKS_PER_SEC >= 0.8 ) { puts ("-1" ); exit (0 ); }
if (pos == len + 1 ) { if (Check ()) { T.Out (); exit (0 ); } return ; }
opt[pos][0 ] = 'a' ; opt[pos][1 ] = 'c' ; str[pos] = 'b' ; Dfs (Jump[pos]);
opt[pos][0 ] = 'b' ; opt[pos][1 ] = 'c' ; str[pos] = 'a' ; Dfs (Jump[pos]);
}
int main () {
File ();
n = len = read (); read (); scanf ("%s" , str + 1 ); Last = len + 1 ;
Fordown (i, len, 1 )
if (str[i] == 'x' ) { Jump[i] = Last; Last = i; }
else { int cnt = 0 ; For (j, 0 , 2 ) if (str[i] != 'a' + j) { opt[i][cnt ++] = 'a' + j; } }
m = read ();
For (i, 1 , m) {
int u, v; char colu, colv; scanf ("%d %c %d %c" , &u, &colu, &v, &colv);
lt[i].u = u; lt[i].v = v; lt[i].colu = tolower (colu); lt[i].colv = tolower (colv);
}
Dfs (Last); puts ("-1" );
return 0 ;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】