版权声明:欢迎转载评论 https://blog.csdn.net/m0_37809890/article/details/81278530
说实话这天的题很不友好,而且和贪心分治感觉联系不是很大
Nike的区间问题
给一个1~n的排列a1,a2,⋯,an。你希望知道有多少个子区间[l,r],满足u≤max(al,al+1,⋯,ar−1,ar)≤v
在一个排列中,区间最大值为k的区间个数等于
从1到n依次访问每个数的位置,用并查集依次维护每个数左边和右边第一个比它本身大的数的位置.
将u到v的结果相加即为答案.
复杂度O(n)
int save[M]; //下标是位置,存储的是值
int loca[M]; //下标是值,存储的是位置
int lef[M],rig[M]; //下标是位置,存储的是位置
int getfa(int *arr,int p)
{
return arr[p]==p?p:arr[p]=getfa(arr,arr[p]);
}
int main(void)
{
int n,u,v;
while(scanf("%d%d%d",&n,&u,&v)!=EOF)
{
save[0] = save[n+1] = MOD;
lef[n+1] = rig[n+1] = n+1;
for(int i=1;i<=n;i++)
{
save[i] = read();
loca[save[i]] = i;
lef[i] = rig[i] = i;
}
ll ans = 0;
for(int num=1;num<=v;num++)
{
int id = loca[num];
lef[id] = getfa(lef,id-1);
rig[id] = getfa(rig,id+1);
if(num>=u)
ans += (id-lef[id])*(rig[id]-id);
}
cout << ans << endl;
}
return 0;
}
Nike的序列问题
给定一个数列,求2^n个子序列和的中位数.
一个结论题,设m=数列之和/2,则对于每个子序列,总有一个互补的子序列使得它们的和为2m且一个小于m,一个大于m.所以m就是答案
int n;
while(scanf("%d",&n)!=EOF)
{
ll ans=0;
while(n--)
ans+=read();
cout << ans/2 << endl;
}
Nike开加油站
从n个数里删去m个,使得排序后数之间最小的差最大,求这个最大的最小差.
最小值最大问题,二分.
int save[M];
int check(int p, int n, int m)
{
int now = 0;
for(int i=0;i<=n;i++)
{
if(save[i] - now < p)
m--;
else
now = save[i];
}
return m>=0;
}
int main(void)
{
int len=read(),n=read(),m=read();
for(int i=0;i<n;i++)
save[i] = read();
save[n] = len;
int l=1,r=len,ans=-1;
while(l<=r)
{
int mid = (l+r)>>1;
if(check(mid,n,m)) ans = mid, l = mid + 1;
else r = mid - 1;
}
printf("%d\n",ans );
return 0;
}
复习一下二分的写法