题目链接:Problem - D - Codeforces
思路:
这道题的规律其实还是挺明显的。可以发现最长的那根头发无论有多少根,只需要一次操作就够了,直接覆盖全头,因为对应的操作只能剪掉比它高的头发。但是如果看次长的那根头发,就不一样了,为了避免把之前处理好的最长的头发给剪短,对应的操作就不能横跨最长的那根头发,可以说,操作区间被比它长的头发给截断了。一个长度头发对应的操作区间不能横跨比他高的头发,针对这点下手就可以求出每个长度的头发所需的操作次数,再对照已有的操作次数就可以判断能否成功了。
实现:
思路出的很快,但是在如何算出每个头发所需操作的时候我犯难了。
一开始我想用分治法来实现,从最大长度的 头发开始,以最长的头发所在的位置把整个头分为一个个区间,然后根据每个区间内是否有比其次高的头发来统计出比其次长度的头发所需的操作次数,然后再以次高的头发为依据再对区间进行细分,再次执行。但是仔细一想这个写法太呆了,很难写,于是我就尝试换一个角度来处理这个问题。
然后我就想到了一个线性的做法。这次不同最高的头发开始,我们从低的头发开始考虑。低的头发在遇到高的头发之后,对应的操作就被切断了,因此只需要遍历一遍需要的头发长度然后遇到比他高的之后就把对应的操作次数减一,然后更新状态,重置为没访问过这个长度的头发,继续遍历。思路清晰,写法简单,开干!
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define all(a) a.begin(), a.end()
#define pb push_back
#define pii pair<int, int>
#define endl '\n'
void solve()
{
priority_queue<int, vector<int>, greater<int>> q;//用优先队列来记录访问过的头发长度
vector<int> a;//现有的头发长度
vector<int> b;//需要的头发长度
map<int, int> mp;//统计操作的次数
bool ok = 0;
int i, j;
int n, m;
cin >> n;
for (i = 0; i < n; i++)//输入现有头发
{
int x;
cin >> x;
a.pb(x);
}
for (i = 0; i < n; i++)//输入需要头发
{
int x;
cin >> x;
b.pb(x);
}
cin >> m;
for (i = 0; i < m; i++)//输入操作次数
{
int x;
cin >> x;
mp[x]++;
}
for (i = 0; i < n; i++)//如果这个点需要被修改,但是没有对应的操作
{ //明显就不可以了
if (a[i] != b[i] && mp[b[i]] == 0)
{
ok = 1;
break;
}
}
map<int, int> tmp;//记录优先队列中现有的数字,保证每个长度的头发只有一个
for (i = 0; i < n; i++)//因为一次操作可以处理一个区间内的多根头发
{
if (a[i] < b[i])//如果现有长度小于需要长度明显无法实现
{
ok = 1;
break;
}
while (!q.empty() && q.top() < b[i])//遇到比较长的头发之后,就进行处理
{
mp[q.top()]--;//对应的操作减1
tmp[q.top()]--;队列中的这个长度一会会出栈所以更新状态
if (mp[q.top()] < 0)//如果需要操作次数大于拥有的操作次数也无法实现
{
ok = 1;
i += n;
break;
}
q.pop();
}
if (tmp[b[i]] == 0 && a[i] != b[i])//如果这个位置的需求和现有的相等,那么就不需要
{ //耗费操作次数,只发挥了隔断的作用
q.push(b[i]);
tmp[b[i]] = 1;
}
}
while (!q.empty())//因为最后可能没有可以更新的契机,所以要自己处理一下小尾巴
{
mp[q.top()]--;
if (mp[q.top()] < 0)
{
ok = 1;
break;
}
q.pop();
}
if (ok == 0)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}