BalticOI 2010 Matching Bins【权值BIT】
题目解析
是考试的题目。
虽然是个签到题但我还是挣扎了很久,而且我个智障把文件名打错了,签到失败嘤嘤嘤
首先简化一下题意:找到一个最大的,使得至中的数都能在到中匹配到一个比它严格小的数(每个数都只能和一个数匹配)。如果没有这样的,输出。
(也可以理解成左半边的数去在右半边找一个比它大的数,是一样的,不过题意理解不同后面的思考分析应该也会相应有所变化)
首先考虑暴力枚举,从大到小枚举,然后去它是否符合情况。
的范围是
是最大值第一次出现的位置,因为最大值不可能出现在前半部分,没有箱子可以装它。注意存在整个数列没有的数据。(出题人大骗子嘤嘤嘤
下面来考虑一下这个该怎么写。
贪心地想,我们应该给每个右半部分的数安排一个小于它的最大的数,暴力做这个的话,我们就要每次排序,然后,想想就很可怕。
对于这种查找左边小于某个数的问题,我们可以考虑一下权值树状数组。
我们把左半边的数插进权值里面,然后对于右半边的数查询有多少个比它小的值。
由于左半边的数可能会被“用掉”,所以我们把右半边的数从小到大排序,然后查询有多少个比它小的左半边的数。
而对于第个数而言,它前面有个数用掉了左半边的一些数,而且用掉的这些数一定是小于它自己的(用掉的这些数小于它前面的数),所以查到的数的个数减去是第个数可以用到的数,如果这个数值小于,那么这个是不可行的。
然后你就得到了的好成绩。
bool check(int x)
{
if(x==0) return 1;
//memset(tree,0,sizeof(tree));
//for(int i=1;i<=x;i++)
// Update(a[i],1);
if(x!=lim) Update(a[x+1],-1);
memcpy(b,a,sizeof(a));
sort(b+x+1,b+2*x+1);
for(int i=x+1;i<=2*x;i++)
{
int tmp=Query(b[i]-1)-(i-x-1);
if(tmp<=0) return 0;
}
return 1;
}
而整个变化的过程中,左半边的数可以通过权值维护,可以可持续发展。但是右边的数的话,我们每次要排序,要查询,最坏复杂度可以达到。而我们又必须要重新排序再枚举右半边的数才能知道它具体能不能匹配得到一个数字。
我们想想能不能让右半边的数也可持续发展一下,换句话说,能不能让查询与排序无关。
还是利用权值,把右边的数的查询变成一个修改,而现在的变化只会影响到这些位置:(假设变成,这里显然顺着做要顺一点,不用提前插入一些元素
- 加入右半部分
- 从前半部分跳到了右半部分
对应以下修改:
- (跑走了,所以把它之前的查询撤回
如何判断当前的是否可行呢?
查询权值的每一个位置(的前缀和),如果有小于零的地方,说明有一个右半部分的数找不到匹配的数,不可行,否则就可行。
所有可行解取即可。
看了一圈没有和我写法一样的,倒是有两位初三的同学写了权值线段树,一个,一个,不过我看了一下,修改的查询的正好和我是相反的,大概是因为权值线段树是区间加减,所以较大的区间应该为才能保证某一段区间和小于零时不成立吧。
别人的写法好巧妙的,但对于我来说不太自然。
瞪了好久才想到优化,不过最后自己独立瞪出来了,还顺手拿了个luogu最优解,在洛谷享受到了coding的快乐(虽然这题很水
理论复杂度应该是,跑得比的算法快我也不知道为什么。
►Code View
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 20005
#define M 1005
#define LL long long
int rd()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
return f*x;
}
int m,n,lim=-1,ans=0;
int a[N],b[N];
int tree[M];
inline int lowbit(int x)
{
return x&(-x);
}
inline int Query(int x)
{
int res=0;
for(;x;x-=lowbit(x))
res+=tree[x];
return res;
}
inline void Update(int x,int val)
{
for(;x<M-5;x+=lowbit(x))
tree[x]+=val;
}
int main()
{
//freopen("bins.in","r",stdin);
//freopen("bins.out","w",stdout);
m=rd(),n=rd();
m++;
for(int i=1;i<=n;i++)
{
a[i]=rd()+1;
if(a[i]==m&&lim==-1)
lim=i;
}
lim=min(lim,n/2);
//if(lim==-1) lim=n/2;
//数据里居然有整个数列都没有m的 出题人大骗子嘤嘤嘤
//其实这个m就是来告诉你值域很小的吧 可这也不需要专门输入一个m啊 直接在数据范围里说不好么233
for(int k=1;k<=lim;k++)
{
Update(a[2*k]-1,-1);
Update(a[2*k-1]-1,-1);
Update(a[k]-1,1);//a[k]不在后半部分了 得撤回
Update(a[k],1);
bool flag=0;
for(int i=1;i<=m;i++)
{
int tmp=Query(i);
if(tmp<0)
{
flag=1;
break;
}
}
if(!flag) ans=max(ans,k);
}
printf("%d\n",ans);
return 0;
}
►Code View 75pts TLE
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 20005
#define M 1005
#define LL long long
int rd()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
return f*x;
}
int m,n,lim=-1;
int a[N],b[N];
int tree[M];
inline int lowbit(int x)
{
return x&(-x);
}
inline int Query(int x)
{
int res=0;
for(;x;x-=lowbit(x))
res+=tree[x];
return res;
}
inline void Update(int x,int val)
{
for(;x<M-5;x+=lowbit(x))
tree[x]+=val;
}
bool check(int x)
{
if(x==0) return 1;
//memset(tree,0,sizeof(tree));
//for(int i=1;i<=x;i++)
// Update(a[i],1);
if(x!=lim) Update(a[x+1],-1);
memcpy(b,a,sizeof(a));
sort(b+x+1,b+2*x+1);
for(int i=x+1;i<=2*x;i++)
{
int tmp=Query(b[i]-1)-(i-x-1);
if(tmp<=0) return 0;
}
return 1;
}
int main()
{
//freopen("bins.in","r",stdin);
//freopen("bins.out","w",stdout);
m=rd(),n=rd();
for(int i=1;i<=n;i++)
{
a[i]=rd();
if(a[i]==m&&lim==-1)
lim=i;
}
lim=min(lim,n/2);
if(lim==-1) lim=n/2;
for(int i=1;i<=lim;i++)
Update(a[i],1);
for(int k=lim;k>=0;k--)
{
if(check(k))
{
printf("%d\n",k);
break;
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现