link

CCF 目前在 CSP-J 出过的唯一蓝题,也是个 nggyu 题。

题目大意

n 个人进行接龙游戏,每人有一个长为 \(l_i\) 的序列 \(S_i\)

规则

  1. 接龙起始序列为 \(\{1\}\)

  2. 每轮进行接龙的人不能是上一轮的人。

  3. 当前玩家在自己的序列中选出一个满足接龙条件的连续子序列。

  4. 选出的子序列长度必须在 [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;
}

干脆改名叫接奶龙得了。

完结撒花