/*
这题的思路据是二分+贪心,二分枚举所有解,贪心判断能否考完
注意,这题自己写代码时,出了一个很隐蔽的bug,当时本来已经把变量 n、m 定义为全局变量了,但居然在main函数里又定义了一次,这就相当于屏蔽了全局变量,虽然输入是正确的,但是,在judge()函数判断能否考完的过程中,所用的n、m都是错的(因为此时真正的是作为局部变量出现的,但judge()函数种使用的为全局)
以及注意,一般情况下,是不建议定义全局变量的,这题其实不定义也未尝不可,只是judge()函数就要传递3个参数了
BTW,其实入门经典里有提到过,全局变量被屏蔽,可能会导致错误。我以为我还算是仔细,今天看来,其实我也还是很粗心的...
最初写这题总结的时候,我还不会二分法,参考了blog:
http://www.cnblogs.com/westwind1005/p/5976882.html
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N];
int d[N];
int pass[N];
int n, m;
int judge(int x)
{
memset(pass, 0, sizeof(pass));
int pre = x - 1;
for (int i = x; i >= 1; i--) //从后往前遍历天数
{
pre = min(pre, i - 1); //不断更新为这门考试的准备时间,最多也只能是i - 1天的复习时间
if (d[i] && !pass[d[i]] && a[d[i]] <= pre)
{
pass[d[i]] = 1;
pre -= a[d[i]] + 1; //+1为下次考试所占的一天,若下次要考试,则下次的准备时间,除了要减去这次考试的准备时间,还要减去下次作为考试的那天
}
}
for (int i = 1; i <= m; i++)
if (!pass[i]) return 0;
return 1;
}
int main()
{
while (cin >> n >> m)
{
int ans = -1;
for (int i = 1; i <= n; i++) cin >> d[i];
for (int i = 1; i <= m; i++) cin >> a[i];
int l = 1,r = n;
while (l <= r)
{
int mid = (l + r) >> 1;
if (judge(mid))
{
ans = mid; r = mid - 1;
}
else
{
l = mid + 1;
}
}
cout << ans << endl;
}
return 0;
}
/*
法二
后来在另外一个blog上,看到judge函数的另外一种写法,这种写法比较容易理解,所以我也自己写了一次
思路来自: http://blog.csdn.net/qq_34374664/article/details/52853212
BTW,上面那篇博客里,博主对这题的解释十分详细,比我自己的代码的解释详细多了...
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N];
int d[N];
int pass[N];
int n, m;
int judge(int x)
{
memset(pass, 0, sizeof(pass));
int sum = 0, cnt = 0; // cnt 用来记录通过的科目数,sum用来目前还需要在前面找多少天复习
for (int i = x; i >= 1; i--) //从后往前遍历天数
{
if (d[i] && !pass[d[i]]) // 如果可以考试且这门课没考,则考
{
pass[d[i]] = 1;
sum += a[d[i]];
cnt++;
}
else if (sum > 0) //如果还需要从前面抽出时间复习,那么就用这天复习,否则就当作休息;注意一定要当前需要前面sum天复习,sum才能自减,如果sum本来就为0,就不可减了,否则WA
{
sum--;
}
}
if (sum > 0|| cnt < m) return 0;
return 1;
}
int main()
{
while (cin >> n >> m)
{
int ans = -1;
for (int i = 1; i <= n; i++) cin >> d[i];
for (int i = 1; i <= m; i++) cin >> a[i];
int l = 1,r = n;
while (l <= r)
{
int mid = (l + r) >> 1;
if (judge(mid))
{
ans = mid; r = mid - 1;
}
else
{
l = mid + 1;
}
}
cout << ans << endl;
}
return 0;
}
/*
法三:
法三利用了栈,我觉得是三种写法总最容易懂的,但似乎也就没有前两种简洁了。
这三种方法,本质上是一样的,只是写法的区别,思路上差异不大
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N];
int pass[N];
int n, m;
struct day
{
int test, have; //变量分别表示:当天能考的科目,已为该科目准备了多久
day()
{
have = 0;
}
}d[N];
int judge(int x)
{
int ans = 0; // 记录有几门科目考完
memset(pass, 0, sizeof(pass));
stack<day> s;
day now; // 当前天数
for (int i = x; i >= 1; i--)
{
if (d[i].test && !pass[d[i].test]) // 从后往前遍历,如果某天有考试,且还没考,那就考这科
{
pass[d[i].test] = 1;
s.push(d[i]);
}
else if ( !s.empty() ) // 否则,取出最近的一次(没准备好)的考试,这天用来准备
{
now = s.top();
s.pop();
now.have++;
if ( now.have == a[now.test] ) //如果需要的准备时间,全都准备好了,则真正出栈。否则,准备时间子增后,再次入栈
ans++;
else s.push(now);
}
}
if (ans == m) return 1;
return 0;
}
int main()
{
while (cin >> n >> m)
{
int ans = -1;
for (int i = 1; i <= n; i++) cin >> d[i].test;
for (int i = 1; i <= m; i++) cin >> a[i];
int l = 1,r = n;
while (l <= r)
{
int mid = (l + r) >> 1;
if (judge(mid))
{
ans = mid; r = mid - 1;
}
else
{
l = mid + 1;
}
}
cout << ans << endl;
}
return 0;
}