Problem J. 屠龙勇者(尺取法)

Problem J. 屠龙勇者

又是一个俗套的故事,吉奥马丘国的公主被恶龙抓走了。作为吉奥马丘国最出名的勇者,ErvinXie
理所当然的担负起了拯救公主,成为屠龙勇士的责任。ErvinXie 极其强大,他攻无不克、战无不胜,对
付恶龙简直就是以大欺小,恃强凌弱、虎入羊群、泰山压卵、席卷八方、翻手为云、覆手为雨。所以他击
败恶龙完全不需要什么准备。但是,传说屠龙勇士终将成为恶龙。ErvinXie 为了自己下半生的幸福,当
然不能容忍自己成为一条恶龙。所以,ErvinXie 从吉奥马丘国的炼金大师 XIRHXQ 那里学习了驱魔法
阵的制作方法,同时炼金大师 XIRHXQ 好心的给 ErvinXie 提供了两套一次性传送阵法。ErvinXie 为了
凑齐驱魔法阵的布阵材料,需要前往竖炬劫沟旁的季蒜脊河里采集材料。季蒜脊河是一条神奇的河,它
里面藏着很多奥秘。季蒜脊河里每一米都有且仅有着一个特殊的炼金材料。为了能够成功布置驱魔法阵,
同时尽快救出公主。ErvinXie 打算使用一次性传送阵法传送到季蒜脊河中某一个位置(ErvinXie 可以指
定是哪一个位置),然后在河中收集炼金材料。收集完材料后,立马传送去恶龙谷拯救公主。
已知这个世界中一共有 k 种炼金材料,分别使用 1, 2, 3…k 编号。
炼金法阵的布置方法是将相应炼金材料按直线摆放就能成功布置。法阵一共需要 s 个炼金材料, 法阵
的布置方法通过 a1, a2, a3 · · · as 给出,其中 ai 代表的是炼金材料种类。可能存在不同位置需要摆放相同
种类炼金材料的情况。那么这种材料就需要收集多个。
季蒜脊河的长度为 len, 从源头开始直到末尾,每米存在的炼金材料为 b1, b2, b3 · · · blen。bi 是炼金材
料的种类。
问 ErvinXie 至少需要经过多少个炼金材料(包含收集的)才能去屠龙拯救公主。当然,如果 ErvinXie
注定收集不全材料,他仍然会去拯救公主,和公主在一起… 即使是以恶龙的身份。
例如:季蒜脊河 5 米长,材料为 [1, 2, 1, 3, 2], 法阵的布置方法为 [1, 2, 3, 2],那么 ErvinXie 可以直接
传送到河里的第 2 个位置,往后收集材料,最终按照顺序收集了 [2, 1, 3, 2] 材料后,能够完成法阵,此时
传送去恶龙谷拯救公主。经过了 4 个炼金材料.
PS:不会吧不会吧,你不会认为 ErvinXie 会按收集顺序摆放法阵吧?ErvinXie 只是懒,不是蠢。他
当然知道材料收集够了,摆放可以自己按顺序摆。
Input
第一行一个数字 k,代表这个世界中存在的炼金材料种类数。1 ≤ k ≤ 5000
第二行一个数字 s,代表法阵的长度。1 ≤ s ≤ 106
第三行 s 个数字,代表法阵的布置方式 ai 。1 ≤ ai ≤ k
第四行一个数字 len,代表季蒜脊河的长度。1 ≤ len ≤ 2 × 106
第五行 len 个数字 bi
, 代表季蒜脊河每米存在的炼金材料的种类。1 ≤ bi ≤ k
Output
输出为一行
如果能够收集完材料,输出一个数字,表示 ErvinXie 至少需要经过多少个炼金材料(包含收集的)
才能去屠龙拯救公主。
否则… 输出 “DragonXie” (不含引号)
Examples
standard input standard output
5
4
1 2 3 2
5
1 2 1 3 2
4
5000
1
512
5
1 2 3 4 5
DragonXie
Note
输入数据较大,推荐使用 scanf 或者自行实现快速读

//模拟
#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int N = 2e6 + 10;
const int M = 5005;
queue<int> q;          //利用栈来找最优解,每当队列中前面的元素不必要时,更新队列的长度,队列的长度即为解的值
vector<int> b[M];      //存第i种元素的位置
int s[M], vis[M];      //s存第i元素需要的数目,vis数组存第i元素的数目是否已经满足条件
int add[M], dis[N];    // add数组存第i种元素现在还在队列中的第一个元素,dis标记第i个位置能不能舍去的
int main() {
    ios::sync_with_stdio(false);
    int n, m, i, j, k, ans = inf, a, len = 0;
    cin >> k >> n;
    for (i = 0; i < n; i++) {
        cin >> a;
        if (s[a] == 0) len++;
        s[a]++;
    }
    cin >> m;
    int flag = 0;
    for (i = 0; i < m; i++) {
        cin >> a;
        q.push(i);
        b[a].push_back(i);
        if (vis[a]) {                       //此时说明现在还在队列中的第一个a元素的位置是可以舍去的
            dis[b[a][add[a]]] = 1;          //标记队列中的第一个a元素的位置可舍去
            add[a]++;                       //更新队列中第一个a元素位置
            while (!q.empty()) {            //更新队列的长度
                int t = q.front();          //队列首元素能舍则舍
                if (dis[t])            
                    q.pop();
                else
                    break;
            }
        } else if (b[a].size() == s[a]) {    //更新答案
            vis[a] = 1;
            flag++;
        }
        if (flag >= len) {
            ans = min(ans, q.back() - q.front() + 1);
        }
    }
    if (flag >= len)
        cout << ans << '\n';
    else
        puts("DragonXie");
    return 0;
}
//尺取法模板
#include <bits/stdc++.h>
using namespace std;
const int M = 2e6 + 10;
const int N = 5005;
int num1[N], num2[N], a[M];
int main() {
    ios::sync_with_stdio(false);
    int k, s, len, n, now, l, r, ans;
    n = now = 0;
    l = r = 1;
    ans = 1e9;
    cin >> k >> s;
    for (int i = 1; i <= s; i++) {
        cin >> a[i];
        if (num1[a[i]] == 0) n++;
        num1[a[i]]++;
    }
    cin >> len;
    for (int i = 1; i <= len; i++) cin >> a[i];
    while (r <= len) {
        while (r <= len && now < n) {
            num2[a[r]]++;
            if (num1[a[r]] == num2[a[r]]) now++;
            r++;
        }
        while (now >= n) {
            if (num1[a[l]] == num2[a[l]] && now >= n) ans = min(ans, r - l);
            num2[a[l]]--;
            if (num2[a[l]] < num1[a[l]]) now--;
            l++;
        }
    }
    if (ans == 1e9)
        puts("DragonXie");
    else
        cout << ans << '\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值