专栏文章
P11230 [CSP-J 2024] 接龙(官方数据) 题解
个人记录参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mir12fxq
- 此快照首次捕获于
- 2025/12/04 14:01 3 个月前
- 此快照最后确认于
- 2025/12/04 14:01 3 个月前
题目介绍
有 个人,第 个人有一个整数序列 作为他的词库。
一次游戏分为 轮,每一轮规则如下:
- 若这不是游戏的第一轮,那么这一轮进行接龙的人不能与上一轮相同,但可以与上上轮或更往前的轮相同。
- 接龙的人选择一个长度在 的 的连续子序列 作为这一轮的接龙序列。若这是游戏的第一轮,那么 需要以元素 开头,否则 需要以上一轮的接龙序列的最后一个元素开头。
- 序列 是序列 的连续子序列当且仅当可以通过删除 的开头和结尾的若干元素(可以不删除)得到 。
- 有 次询问,每次询问包括两个参数 ,表示询问是否存在一个 轮的接龙游戏,满足最后一次接龙的 以数字 结尾。
样例
样例输入
CPP1
3 3 7
5 1 2 3 4 1
3 1 2 5
3 5 1 6
1 2
1 4
2 4
3 4
6 6
1 1
7 7
样例输出
CPP1
0
1
0
1
0
0
提示
【样例 1 解释】
在下文中,我们使用 表示一轮游戏中所有的接龙序列, 表示对应的接龙的人的编号。由于所有字符均为一位数字,为了方便我们直接使用数字字符串表示序列。
- 对于第一组询问,、 是一个满足条件的游戏过程。
- 对于第二组询问,可以证明任务不可完成。注意 、 不是合法的游戏过程,因为此时 。
- 对于第三组询问,、 是一个满足条件的游戏过程。
- 对于第四组询问,可以证明任务不可完成。注意 不是一个合法的游戏过程,因为尽管所有的接龙序列长度均不超过 ,但第二轮和第三轮由同一个人接龙,不符合要求。
- 对于第五组询问,、 是一个满足条件的游戏过程。
- 对于第六组询问,可以证明任务不可完成。注意每个接龙序列的长度必须大于等于 ,因此 不是一个合法的游戏过程。
- 对于第七组询问,所有人的词库均不存在字符 ,因此任务显然不可完成。
【数据范围】
对于所有测试数据,保证:
- ;
- ,,;
- ,;
- ,;
- 设 为单组测试数据内所有 的和,则 。
| 测试点 | 特殊性质 | ||||
|---|---|---|---|---|---|
| 无 | |||||
| 无 | |||||
| A | |||||
| A | |||||
| B | |||||
| B | |||||
| C | |||||
| C | |||||
| 无 | |||||
| 无 |
特殊性质 A:保证 。
特殊性质 B:保证 。
特殊性质 C:保证在单组测试数据中,任意一个字符在词库中出现次数之和均不超过 。
原题链接
100pts 思路讲解
算法:动态规划
时间复杂度:
通过搜索可以拿 分,可以考虑运用记忆化搜索来优化程序效率,进而可以想到从动态规划的角度思考这道题的解法。
对于第 个数字序列 ,将其长度记为 。不妨认为 的下标范围为 。
我们定义状态 表示在第 轮中,能否以第 个数字序列的第 个位置作为起点(其中第 维和第 维的状态总数不超过 )。
定义状态 表示在第 轮中,能否以第 个数字序列的第 个位置作为终点(其中第 维和第 维的状态总数不超过 )。
动态规划的边界条件:若 ,则 ;否则 。
动态规划的答案表示:对于询问 ,若存在 满足 并且 ,则说明存在一种接龙方案,否则说明不存在。
分析状态转移方程
转移可以分为两类:
-
转移到同一轮 。
-
转移到下一轮 。
第一类转移的暴力实现需要 的时间复杂度,因为对于每一个 的位置,需要将 都标记为 。
第二类转移的暴力实现需要需要 的时间复杂度,因为对于每一个 为 的位置,我们需要找到所有的 满足 且 ,将 设置为 。
上面两种转移的暴力实现总时间复杂度为,会超时,需要进一步优化。
转移到同一轮 的优化
我们发现,对于每一个 为 的位置,需要将连续一段区间的 标记为 。那么我们可以通过一个长度为 的差分数组 比较快地实现连续一段区间标记。
如果某一轮中 为 ,我们就对第 个数字序列在区间 进行加 标记。这一操作表示能以 区间的数字作为这一轮接龙的结尾。
然后对 每个位置进行前缀和处理,这样只要前缀和后数组 位置 的值大于 ,就知道 的值应该为 。
代码+注释
CPP#include<bits/stdc++.h>
using namespace std;
typedef int ll;
const ll N=200010,M=110;
ll t,n,m,k,len[N],num[N],l,r;
vector<ll> a[N];
vector<bool> dp[N],f[N];
bool vis[N],ans[M][N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
{
cin>>len[i];
a[i].resize(len[i]),dp[i].resize(len[i]),f[i].resize(len[i]);
for(int j=0;j<len[i];j++)
{
cin>>a[i][j];
if(a[i][j]==1)
{
dp[i][j]=true;
}
else
{
dp[i][j]=false;
}
}
}
memset(ans,false,sizeof(ans));
for(int i=1;i<=100;i++)
{
for(int j=1;j<=n;j++)
{
for(int l=0;l<len[j];l++)
{
num[l]=0;
}
for(int l=0;l<len[j];l++)
{
if(dp[j][l])
{
num[l+1]++;
if(len[j]>l+m)
{
num[l+m]--;
}
}
}
for(int l=0;l<len[j];l++)
{
if(l>0)
{
num[l]+=num[l-1];
}
if(num[l]>0)
{
f[j][l]=true;
}
else
{
f[j][l]=false;
}
ans[i][a[j][l]]|=f[j][l];
}
}
memset(vis,false,sizeof(vis));
for(int j=1;j<=n;j++)
{
for(int l=0;l<len[j];l++)
{
dp[j][l]=vis[a[j][l]];
}
for(int l=0;l<len[j];l++)
{
if(f[j][l])
{
vis[a[j][l]]=true;
}
}
}
memset(vis,false,sizeof(vis));
for(int j=n;j>=1;j--)
{
for(int l=0;l<len[j];l++)
{
dp[j][l]=dp[j][l]||vis[a[j][l]];
}
for(int l=0;l<len[j];l++)
{
if(f[j][l])
{
vis[a[j][l]]=true;
}
}
}
}
while(k--)
{
cin>>l>>r;
cout<<ans[l][r]<<"\n";
}
}
return 0;
}
100pts 记录
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...