luogu#P7877. 「SWTR-7」Spider Solitaire

    ID: 11873 远端评测题 1000ms 512MiB 尝试: 0 已通过: 0 难度: 6 上传者: 标签>模拟Special JudgeO2优化图的建立,建图拓扑排序洛谷月赛

「SWTR-7」Spider Solitaire

题目背景

题目描述下方有简化题意。


小 A 在玩蜘蛛纸牌。
为了方便你理解蜘蛛纸牌,小 A 给出了简化后的游戏规则:

  • 一副牌有 nn 张,从小到大分别为 1,2,,n1,2,\cdots,n
  • 现有 mm 个牌堆,11 副牌。每个牌堆中有 00 张或多张牌。
  • 定义「龙」a1,a2,,ada_1,a_2,\cdots,a_d 为满足 ai1=ai+1 (1i<d)a_i-1=a_{i+1}\ (1\leq i<d) 的任意多张连续的牌。一张牌也是一个「龙」。
  • 一组连续的牌可以移动,当且仅当这组牌形成了一个「龙」,且「龙」在牌堆的最右边
  • 一组连续的牌只能移动到一个非空牌堆的最右边,且必须满足可以与该非空牌堆最右边的「龙」构成一条更大的「龙」
  • 游戏胜利,当且仅当所有的 nn 张牌形成了一个「龙」。

例如当 m=3m=3n=9n=9,局面为

9 8 4 3 2 1
7 5
6

时,第一个牌堆最右边的 4 3 2 1 形成了一个「龙」,所以 4 3 2 1 可以移动。将 4 3 2 1 移动到第二个牌堆的最右边,此时局面为

9 8
7 5 4 3 2 1
6

接下来将第二个牌堆右边的 5 4 3 2 1 移动到第三个牌堆的最右边,此时局面为

9 8
7
6 5 4 3 2 1

接下来将第三个牌堆的 6 5 4 3 2 1 移动到第二个牌堆的最右边,此时局面为

9 8
7 6 5 4 3 2 1
\

接下来将第二个牌堆的 7 6 5 4 3 2 1 移动到第一个牌堆的最右边,此时牌堆为

9 8 7 6 5 4 3 2 1
\
\

因为所有 99 张牌形成了一个「龙」,所以游戏胜利。

题目描述

给定一个蜘蛛纸牌初始局面,小 A 想知道能否获得胜利。若能,请输出 YES\texttt{YES} 以及获胜所需的最小步数。否则输出 NO\texttt{NO}

小 A 还想知道,对于每张牌 ii,如果要移动 ii 至少需要多少步,包括移动 ii 的这一步。如果无法移动输出 -1


「简化题意」

mm横向的数堆,数堆里共有 nn 个数,每个数堆里有 00 或多个数。所有数堆里的数组成了 1n1\sim n 中的所有数。

你可以将一个数堆最右边递减且公差为 1-1连续若干个数 a1,a2,,aca_1,a_2,\cdots,a_c 按照原来的顺序移到另外一个非空数堆的最右边,当且仅当该非空数堆最右边的一个数 b=a1+1b=a_1+1

求将所有的 nn 个数都移动到同一个数堆且满足从左往右依次递减的最小步数。如果无解输出 NO\texttt{NO}

此外,你还需要对于每个数 ii,输出如果要移动 ii 至少需要多少步。

输入格式

第一行一个整数 TT,表示子任务编号。
第二行两个整数 n,mn,m,分别表示一副牌的张数和牌堆的个数。
接下来 mm 行,每行首先一个整数 cc 表示该牌堆中牌的个数,接下来 cc 个整数 b1,b2,,bcb_1,b_2,\cdots,b_c 从左到右描述这个牌堆。

输出格式

如果能够获胜,在第一行输出 YES\texttt{YES}第二行输出最少步数。否则输出一行 NO\texttt{NO}

无论是否能够获胜,你还需输出 nn 行,第 ii 行一个介于 1-1nn 之间的整数,表示移动 ii 至少需要多少步。

0
9 3
6 9 8 4 3 2 1
2 7 5
1 6

YES
4
1
1
1
1
1
2
3
-1
-1

0
13 4
4 13 10 1 7
3 11 4 8
4 6 5 3 2
2 12 9

YES
10
2
2
2
3
3
3
1
1
3
6
7
8
-1

0
5 1
5 5 4 3 2 1

YES
0
-1
-1
-1
-1
-1

0
17 10
2 12 14
1 3
3 1 13 15
0
2 9 8
1 5
3 16 7 6
2 11 2
1 4
2 17 10

YES
14
4
1
1
1
1
1
1
1
1
2
3
4
3
1
2
4
-1
0
13 4
4 10 1 13 7
4 11 12 4 8
4 6 5 3 2
1 9

NO
-1
2
2
3
3
3
1
1
-1
-1
6
5
-1

提示

「样例 1 说明」

因为 1,2,3,4,5 可以直接移动,所以至少需要 1 步即可移动。
因为需要先将 5 移至 6 右侧,6 才能移动,所以至少需要 2 步即可移动。
因为需要先将 5 移至 6 右侧,再将 4,3,2,1 移至 5 右侧,7 才能移动,所以至少需要 3 步即可移动。
显然 8,9 无法移动。

「Special Judge」

本题使用 Special Judge,请严格遵守输出格式

  • 如果你正确输出对能否获胜的判定,且如果能够获胜,你正确输出最小步数,你将获得该测试点至少 40%40\% 的分数。
  • 在上一部分的基础上,如果你正确输出移动每张牌的最小步数,你将获得该测试点剩下 60%60\% 的分数。也就是说,如果你上一部分输出错误,你在这一部分也不会获得任何分数。
  • 如果你的输出格式错误,你将获得该测试点 0%0\% 的分数,包括但不限于只输出对能否获胜的判定
  • 需要特别注意的是,如果你不能正确求出移动每张牌的最小步数,请随机输出 [1,n][-1,n] 之间的任意整数,否则你将获得该测试点 0%0\% 的分数。
  • 每行结束后你都需要输出换行符,包括最后一行

checker 将在题目最后给出。

「数据范围与约定」

本题采用捆绑测试。

  • Subtask #0(0 points):是样例。
  • Subtask #1(15 points):n3×103n\leq 3\times 10^3m=2m=2
  • Subtask #2(15 points):bi>bi+1 (1i<c)b_i>b_{i+1}\ (1\leq i<c)n3×103n\leq 3\times 10^3
  • Subtask #3(25 points):n14n\leq 14m=3m=3
  • Subtask #4(30 points):n3×103n\leq 3\times 10^3
  • Subtask #5(15 points):无特殊限制。

对于 100%100\% 的数据,1n,m5×1041\leq n,m\leq 5\times 10^4。时间限制 1s,空间限制 512MB。

「题目来源」

Sweet Round 07 D。
idea & solution & data:Alex_Wei;验题:chenxia25


以下是 checker,你需要有 testlib.h 才能成功编译。

#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define pii pair <int,int>
#define fi first
#define se second
#define pb emplace_back
#define mp make_pair 
#define vint vector <int>
#define vpii vector <pii>
#define all(x) x.begin(),x.end()
#define sor(x) sort(all(x))
#define rev(x) reverse(all(x))
#define mem(x,v) memset(x,v,sizeof(x))

#define rint inf.readInt
#define reof inf.readEof()
#define reoln inf.readEoln()
#define rspace inf.readSpace()

// wrong answer : quitf(_wa,"The answer is wrong")
// accepted :  quitf(_ok,"The answer is correct")
// partly correct : quitp(0.5,"The answer is partly correct")

int main(int argc,char* argv[]){
	registerTestlibCmd(argc,argv);
	
	string jans=ans.readToken();
	string pans=ouf.readToken(jans);
	int sub=rint(),n=rint(),diff=0;
	
	if(jans=="YES"){
		int jstep=ans.readInt();
		int pstep=ouf.readInt();
		if(jstep!=pstep)quitf(_wa,"The answer is wrong");
	}
	
	for(int i=1;i<=n;i++){
		int jans=ans.readInt();
		int pans=ouf.readInt();
		if(jans!=pans)diff=1;
	}
	
	while(!ouf.seekEof())ouf.readToken();
	while(!inf.seekEof())inf.readToken();
	while(!ans.seekEof())ans.readToken();
	if(diff)quitp(0.4,"The answer is partially correct");
	else quitf(_ok,"OK, you AK IOI");
	
	return 0;
}