link
CCF 目前在 CSP-J 出过的唯一蓝题,也是个 nggyu 题。
题目大意
n 个人进行接龙游戏,每人有一个长为 \(l_i\) 的序列 \(S_i\)。
规则
-
接龙起始序列为 \(\{1\}\)。
-
每轮进行接龙的人不能是上一轮的人。
-
当前玩家在自己的序列中选出一个满足接龙条件的连续子序列。
-
选出的子序列长度必须在 [2,k] 范围内,k 是定值。
有 q 次询问,每次询问给出 \(r_i\ ,c_i\)
求恰好进行 \(r_i\) 轮接龙的情况下,是否存在一种情况,使最终接龙序列的末位字符为 \(c_i\),存在输出 1,不存在输出 0。
正解
奶龙题就懒得说部分分了,直接考虑 dp。
状态设计
考虑设计一个二维 dp [ i ] [ j ] ,表示接龙序列末位字符为 j 时可以进行第 i 轮接龙的玩家,如果有多位就用 0 表示,所以初始值全部设为 -1。
看一眼题目范围发现时间空间都卡的刚刚好,且询问时可 O(1) 回答,所以这就是正解😋。
状态转移
应该是个人就会。
第一层枚举进行轮数,很明显每一轮都由上一轮转移来。
第二层枚举每个玩家序列的字符更新答案,直接将上一轮满足条件的末尾字符作为这一轮的起始点,往后扫 k 个打上标记,遇到打过标记的就直接给它置为 0(表示有多个玩家满足条件)。
求完 dp 数组之后直接 O(1) 回答询问即可。
AC代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+9;
const int maxm=109;
int T,n,k,q,r,c;
int tail[maxm][maxn];
vector<int> S[maxn];
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&k,&q);
for(int i=1,l;i<=n;i++){
scanf("%d",&l);S[i].clear();
for(int j=1,x;j<=l;j++){
scanf("%d",&x);
S[i].push_back(x);
}
}
memset(tail,-1,sizeof tail),tail[0][1]=0;
for(r=1;r<=100;r++){
for(int i=1;i<=n;i++){
int len=0;
for(auto x:S[i]){
if(len) len--;
if(len){
if(tail[r][x]==-1) tail[r][x]=i;
else if(tail[r][x]^i) tail[r][x]=0;
}
if(~tail[r-1][x]&&tail[r-1][x]^i) len=k;
}
}
}
while(q--){
scanf("%d%d",&r,&c);
puts(~tail[r][c]?"1":"0");
}
}
return 0;
}
干脆改名叫接奶龙得了。