[NOIP2012]借教室(二分答案与差分)
链接:https://ac.nowcoder.com/acm/problem/16564
来源:牛客网
首先考虑的肯定是暴力模拟,然后判断是否在某天存在教室数量小于0的情况,则为不满足
但复杂度为n2,超时
观察到对于每个订单的可满足性具有单调且有界性,即某一个订单为不满足为界,则其后面全不可满足,前面为可满足,那么就确定二分对象为订单,而对于区间操作我们可以利用差分
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e6 + 10;
class node
{
public:
int need;
int l,r;
};
int n, m;
int s[N];
int d[N];
node p[N];
int check(int mid)
{
for(int i=0;i<=n;i++) d[i]=0;
for(int i=1;i<=n;i++)
{
d[i]=s[i]-s[i-1];
}
for(int i=1;i<=mid;i++)
{
int l = p[i].l;
int r = p[i].r;
int w=p[i].need;
d[l]-=w;
d[r+1]+=w;
}
for(int i=1;i<=n;i++)
{
d[i]=d[i]+d[i-1];
if(d[i]<0) return false;
}
return true;
}
signed main()
{
int ans=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>s[i];
}
for(int i=1;i<=m;i++)
{
int a,b,c;cin>>a>>b>>c;
p[i]={a,b,c};
}
int l=1;int r=m+1;
while(l<r)
{
int mid=(l+r)>>1;
if(!check(mid))r=mid;
else l=mid+1 ;
}
if(l==m+1) cout<<0<<endl;
else
cout<<-1<<endl<<l<<endl;
}
NC14301 K-th Number
链接:https://ac.nowcoder.com/acm/problem/14301
来源:牛客网
一眼顶针,毫无头绪() ()即使知道是二分答案加尺取也不知道怎么写,看了题解才有大致的思路
首先明确两个性质:
- 对于已经存在的第k大的数,在添加进一个小于他的数后,其位置不发生改变
- 而在添加进一个大于他的数后,其位置变为k-i,i为大于他的数的数量
条件1:其次明确,所有存在于B数组中的数都是由a中取得的,且B中必定存在m个数大于答案
条件2:于是易得这m个数也是通过题目要求的k区间第k大数取得的,很容易联想到我们需要枚举区间,区间满足我在区间内取到的数字到B中必定能大于答案,且恰好有m个区间(因为在B中为第m大的数)
思路:那么就可以二分猜测答案,那么怎么check呢,可以利用尺取法枚举区间,首先区间要满足第k大的数字大于等于我的猜测答案(区间内取数才能满足条件2),然后记录有多少个这样的区间,若区间数量大于m,那么可以增大答案,减少区间数量看是否满足,反之亦然,直到得到解。
代码如下
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e6 + 10;
const int INF = 1e9;
int n, k, m;
int s[N];
int answer;
int check(int mid)
{
int cnt = 0;
int ans = 0;
int j = 1;
for (int i = 1; i <= n; i++)
{
if (s[i] >= mid)
cnt++;
if (cnt == k)
{
ans += n - i + 1;
while (j <= i && s[j] < mid)
ans += n - i + 1, j++;
j++;
cnt--;
}
else
continue; // 统计区间数量
}
if (ans >= m)
return true;
else
return false;
}
signed main()
{
int temp;
cin >> temp;
while (temp--)
{
cin >> n >> k >> m;
for (int i = 1; i <= n; i++)
cin >> s[i];
int l = 0;
int r = INF;
while (l < r)
{
int mid = (l + r+1) >> 1;
if (check(mid))
l = mid;
else
r = mid - 1;
}
cout << l << endl;
}
}