Written with StackEdit.
Description
一次考试共有(n)个人参加,第(i)个人说:“有(a_i)个人分数比我高,(b_i)个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数).
Input
第一行一个整数(n),接下来(n)行每行两个整数,第(i+1)行的两个整数分别代表(a_i,b_i.)
Output
一个整数,表示最少有几个人说谎.
Sample Input
3
2 0
0 2
2 2
Sample Output
1
HINT
(100\%)的数据满足: (1≤n≤100000 0≤a_i,b_i≤n.)
Solution
- 一句话题面系列...
- 关键在于条件的转化.有(a)个人严格比他大,(b)个人严格比他小,
- 若将分数从大到小排序,如果这句话为真,那么易知:
- 性质1这个人的分数一定属于([a+1,n-b])中,且这个区间内所有人的分数都相同.
- 性质2这个区间外的分数都与区间内的分数不同.
- 首先可以排除掉区间左端点大于右端点的人,这些话一定为假.
- 对于剩下的人,我们假设这些话都为真,可以得到若干个区间.
- 如果两个区间相交,而又不完全重合,将这两个区间并起来显然可以得到更大的分数相同区间,不符合性质2.而对于完全相同的两个区间,而根据性质1,针对这个区间的话,最多可以有(r-l+1)(区间长度)句话为真.
- 那么可以给每个区间赋一个权值,为这个区间的重复次数与区间长度的最小值.我们现在选出若干个不相交的区间,这些区间的最大权值和即为最多说真话的人数.
- 这里就是一个(dp)了.优化方法多样,我选择
无需思考的树状数组.
#include<bits/stdc++.h>
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
{
fh=-1;
jp=getchar();
}
while (jp>='0'&&jp<='9')
{
out=out*10+jp-'0';
jp=getchar();
}
return out*fh;
}
const int MAXN=1e5+10;
int n,tot,unitot;
struct sec{
int l,r;
int w;
bool operator < (const sec& rhs) const
{
return r==rhs.r?l<rhs.l:r<rhs.r;
}
}s[MAXN],p[MAXN];
#define lowbit(x) x&(-x)
int bit[MAXN];
inline void upd(int x,int c)
{
for(;x<=n;x+=lowbit(x))
bit[x]=max(bit[x],c);
}
inline int query(int x)
{
if(x<=0)
return 0;
int res=0;
for(;x;x-=lowbit(x))
res=max(res,bit[x]);
return res;
}
void UniqueSec()
{
for(int i=1;i<=tot;++i)
{
if(i==1 || s[i].l!=s[i-1].l || s[i].r!=s[i-1].r)
++unitot;
p[unitot].w=min(p[unitot].w+1,p[unitot].r-p[unitot].l+1);
p[unitot].l=s[i].l;
p[unitot].r=s[i].r;
}
}
int main()
{
n=read();
for(int i=1;i<=n;++i)
{
int a=read(),b=read();
int l=a+1;
int r=n-b;
if(l<=r)
{
s[++tot].l=l;
s[tot].r=r;
}
}
sort(s+1,s+1+tot);
UniqueSec();
for(int i=1;i<=unitot;++i)
{
int l=p[i].l,r=p[i].r;
int val=query(l-1)+p[i].w;
upd(r,val);
}
int ans=n-query(n);
printf("%d
",ans);
return 0;
}