这里不细讲 AC 代码,这篇题解主要是教像我这种连 bitset
+ 拓扑排序都不会的蒟蒻如何用暴力模拟在比赛中拿到 40 分。
思路
首先,容易想到,我们可以把一条龙抽象成左边是 ,右边是 的一个区间,但是因为龙里的数是从大到小,所以,一般情况下 。
合并也很简单,龙 可以接在龙 后面,当且仅当 的 的 ,合并之后的龙的 的 , 的 。
所以,当我们读入数堆里的一个数 时,先把它作为一条
的龙,如果它可以和上一条龙接在一起,就把它接在一起;否则就把它作为一条新龙丢到 vector
末尾。
此外,我还加了一个细节:当 是读入的数里面的第一个数时,先往
vector
里丢一个 , 的龙,可以和 照常接在一起,以免出现在 vector
里面找不到数的情况。
这里还要特别提醒一下,题目里有一句话,是这样的:
一组连续的牌只能移动到一个非空牌堆的最右边……
这意味着,这道题不能像蜘蛛纸牌那样,用空牌堆来转移!!!
所以,我们可以高兴的继续写暴力了!!!
因为接总是把两条龙接成一条,所以如果有解,把 条龙接成一条一定需要 次操作,所以这道题的 pts 就变成了求龙数量和是否有解两个问题。
求龙数量很简单,只要是在输入第一个数或者新加龙的时候统计一下即可。
而判是否有解我们可以这样:
枚举每一个牌堆最顶上的龙,判断它要接的那条龙是否在牌堆最末端,如果在最末端就把它接上去,否则不能接。因为每一条龙只能接到一条龙的后面,影响不到其他的龙,所以先接哪条后接哪条都一样!!!
思路已经想好了,那该如何实现呢?
暴力的方法就是寻找每个牌堆的最末端,时间复杂度达 ,超时。
但是我们可以用 和 分别记录结尾为 的龙所在的牌堆号和结尾为 的龙在所在的牌堆里的位置,这样,就可以 判断要接的那条龙是否在末尾了。
如果在这 个牌堆里的最后一条龙都无法接到其他龙,则无解,否则,有解。
时间复杂度大概是 , 分稳稳到手!
代码
pts 代码:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
struct lo{
int l, r;
bool operator&(lo a){
return r == a.l + 1;
}
lo operator+(lo a){
return (lo){l, a.r};
}
void operator+=(lo a){
*this = *this + a;
}
}; // 区间结构体
int n, m, k, t, tt, d[50001], f[50001], did = 1;
vector<lo> g[50001];
int main(){
scanf("%d%d%d", &t, &n, &m);
for (int i = 1;i <= m;i ++){
scanf("%d", &t);
for (int j = 1;j <= t;j ++){
scanf("%d", &tt);
if (j <= 1) g[i].push_back((lo){tt, tt + 1}); // 塞入一条没用的龙
lo awa = {tt, tt}, end = *(-- g[i].end());
if (end & awa) *(-- g[i].end()) += awa; // 可以接就接
else d[end.r] = g[i].size(), f[end.r] = i, g[i].push_back(awa), k ++; // 否则,记录上一条龙的信息,新增一条龙,并计数。
}
if (!t) continue;
lo end = *(-- g[i].end());
d[end.r] = g[i].size(), f[end.r] = i, k ++; // 记录信息并计数。
}
for (int _ = 1;_ < k;){
did = 0;
for (int i = 1;i <= m;i ++){
if (!g[i].size()) continue; // 防止 RE
lo end = *(-- g[i].end());
int sr = end.l + 1; // 求出要接到的龙的 r
if (sr > n) continue; // l == n 的龙不能接
int tr = end.r; // 自己的 r
int sf = f[sr]; // 求出要接到的龙所在的牌堆
if (d[sr] == g[sf].size()){ // 如果正好就在末尾,就接上去
*(-- g[sf].end()) += end; // 合并
g[i].pop_back(); // 把原来的删掉
d[tr] = d[sr];
f[tr] = sf;
d[sr] = f[sr] = 0; // 更改信息
did = 1; // 已经接过了,记录并非无解
_ ++; // 已经操作了一次
}
}
if (!did) break; // 如果无解
}
if (did) printf("YES\n%d\n", k - 1); // 有解
else printf("NO\n"); // 无解
for (int i = 1;i <= n;i ++) printf("0\n");
}