CF1779D 每日一题 Boris and His Amazing Haircut

题目链接:Problem - 1779D - Codeforces

input:

7
3
3 3 3
2 1 2
2
1 2
6
3 4 4 6 3 4
3 1 2 3 2 3
3
3 2 3
10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
10
1 2 3 4 5 6 7 8 9 10
3
1 1 1
1 1 2
12
4 2 4 3 1 5 6 3 5 6 2 1
13
7 9 4 5 3 3 3 6 8 10 3 2 5
5 3 1 5 3 2 2 5 8 5 1 1 5
8
1 5 3 5 4 2 3 1
13
7 9 4 5 3 3 3 6 8 10 3 2 5
5 3 1 5 3 2 2 5 8 5 1 1 5
7
1 5 3 4 2 3 1
3
19747843 2736467 938578397
2039844 2039844 2039844
1
2039844

otuput:

YES
NO
YES
NO
YES
NO
YES

题意:

Boris 想理发,其头发可视为大小为 n 的数组 ,每根头发的长度为 ai ,Boris 对于每根头发的理想长度为 bi 。理发师有 m 把剪刀,对于长度为 x 的剪刀,每把剪刀至多可以使用一次(可能存在若干把剪刀长度相同)。

对于每一把长度为 x 剪刀的使用,理发师可以选择任意 l,r,使得 a[l , r] 的所有 ai 变为 min(ai , x)

问在上述规则下,Boris 的所有头发是否都可以达到理想情况。

思路:

首先我们可以知道,不满足条件的情况有两种:

1.存在 i ,使得 ai < bi 

2.存在 x ,长度为 x 的剪刀的数量小于需要使用的数量

第一种情况可以直接处理,第二种情况则需要我们通过处理数据来判断。

实际上 ai 只对处理第一种情况,和判断当前头发是否需要修剪有意义,所以可以放一边不看,重点看 bi。

首先最朴素的修剪方法一定是每根头发配备一把对应理想长度的剪刀,所以我们先处理出来每种长度的头发出现的位置。随后如果我们想将两根理想长度相同的头发同时修剪,需要考虑这两根头发之间是否存在比当前目标长度长的头发,若不存在,则可以合并修剪,否则不合法。对于可以合并修剪的部分,我们将先前存的需要剪刀的数量减一(区间查询最大值有多种方案,这里我使用了树状数组,当然线段树亦可)。

遍历处理后,对于每种长度需要的剪刀数量,判断是否大于拥有的剪刀数量即可。

Code

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define endl "\n"
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int mod=1e9+7;
const int N=2e5+10;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;

int t,n,m;

int a[N],b[N];
int h[N];

int lowbit(int x)
{
    return x&(-x);
}

void update(int x)
{
    int lowx,k;
    while(x<=n)
	{
        h[x]=b[x];
        lowx=lowbit(x);
        for(k=1;k<lowx;k<<=1) 
        {
 			h[x]=max(h[x],h[x-k]);       	
		}
        x+=lowbit(x);
    }
}
int query(int x,int y)
{
    int ans=0;
    while(y>=x) 
	{
        ans=max(b[y],ans);
        y--;
        for(;y-lowbit(y)>=x;y-=lowbit(y))
        {
 			ans=max(h[y],ans);       	
		}
    }
    return ans;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--)
	{
		map<int,int> mp;
		map<int,vector<int> > mp1;
		map<int,int> mp2;
		cin>>n;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
		}
		for(int i=1;i<=n;i++)
		{
			cin>>b[i];
			if(a[i]!=b[i])
			{
				mp1[b[i]].pb(i);
			}
			update(i);
		}
		cin>>m;
		int k;
		for(int i=1;i<=m;i++)
		{
			cin>>k;
			++mp[k];
		}
		bool f=1;
		for(int i=1;i<=n;i++)
		{
			if(a[i]<b[i])
			{
				f=0;
				break;
			}
			if(a[i]!=b[i])
				++mp2[b[i]]; 
		}
		for(auto x:mp1)
		{
			int l=0,r=1;
			while(l<x.second.size()&&r<x.second.size())
			{
				if(query(x.second[l]+1,x.second[r]-1)<=x.first)
				{
					++r;
					--mp2[x.first];
				}
				else
				{
					l=r;
					++r;
				}
			}
		}
		for(auto x:mp2)
		{
			if(x.second>mp[x.first])
			{
				f=0;
				break;
			}
		}
		if(f)
			cout<<"YES"<<endl;
		else
			cout<<"NO"<<endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值