第十周总结

这一周我们训练了二分法。

F

Given N numbers, X1, X2, … , XN, let us calculate the difference of every pair of numbers: ∣Xi - Xj∣ (1 ≤ i < j ≤ N). We can get C(N,2)
differences through this work, and now your task is to find the median
of the differences as quickly as you can!

Note in this problem, the median is defined as the (m/2)-th smallest
number if m,the amount of the differences, is even. For example, you
have to find the third smallest one in the case of m = 6.
The input consists of several test cases.
In each test case, N will be given in the first line. Then N numbers are given, representing X1, X2, … , XN, ( Xi ≤ 1,000,000,000 3 ≤ N ≤ 1,00,000 )

题目大意:有N个数,求他们两两相减的绝对值形成的集合中的中位数。
分析: 如果用暴力求出所有的值,数据个数会达到49*10^8,显然不行。
当N为奇数时,中位数右边有N/2个数,当N为偶数时,也是这样。因为这些集合中的数是由两个数相减得到的,固定一个数n1,枚举中位数,另一个数n2等于n1加中位数。大于n2的数相当于中位数右边的数。求出他们的个数,判断条件。

int sum[100002];int lop[16000001],kol[16000001];
int judge(int x,int mid){
    int sum1=0;
    for(int i=1;i<=sum[0];i++)
        {int k=sum+sum[0]+1-upper_bound(sum+1,sum+sum[0]+1,sum[i]+x);//尾座标与第一个大于sum[i]+x坐标之差
        sum1+=k;}
    if(sum1<=mid)//向左推进
        return true;
    else return false;
}
int main(){
    int n;
    while(~scanf("%d",&n)){
    sum[0]=n;
    for(int i=1;i<=n;i++)
        scanf("%d",&sum[i]);
    sort(sum+1,sum+n+1);
    long long num=(n-1)*n/2;
    num/=2;//求中位数右边数的个数
    int maxx=sum[n]-sum[1];
    int l=0;
    while(l<maxx)
    {
        int mid=(l+maxx)>>1;

        if(judge(mid,num))
            maxx=mid;
        else l=mid+1;
    }
    cout<<maxx<<endl;
    }
}

Every year the cows hold an event featuring a peculiar version of
hopscotch that involves carefully jumping from rock to rock in a
river. The excitement takes place on a long, straight river with a
rock at the start and another rock at the end, L units away from the
start (1 ≤ L ≤ 1,000,000,000). Along the river between the starting
and ending rocks, N (0 ≤ N ≤ 50,000) more rocks appear, each at an
integral distance Di from the start (0 < Di < L).

To play the game, each cow in turn starts at the starting rock and
tries to reach the finish at the ending rock, jumping only from rock
to rock. Of course, less agile cows never make it to the final rock,
ending up instead in the river.

Farmer John is proud of his cows and watches this event each year. But
as time goes by, he tires of watching the timid cows of the other
farmers limp across the short distances between rocks placed too
closely together. He plans to remove several rocks in order to
increase the shortest distance a cow will have to jump to reach the
end. He knows he cannot remove the starting and ending rocks, but he
calculates that he has enough resources to remove up to M rocks (0 ≤ M
≤ N).

FJ wants to know exactly how much he can increase the shortest
distance before he starts removing the rocks. Help Farmer John
determine the greatest possible shortest distance a cow has to jump
after removing the optimal set of M rocks.
Line 1: Three space-separated integers: L, N, and M
Lines 2… N+1: Each line contains a single integer indicating how far some rock is away from the starting rock. No two rocks share the same position.

题目大意:有n个石头且农场主能够移除m个石头,求石头间的最大距离。
枚举距离,判断在此距离下满足条件石头间距的数量,以此二分。


#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long ll;
ll sum[512314],er,n,co;
int main() {
    while(~scanf("%lld%lld%lld",&er,&n,&co))
    {
    memset(sum,0,sizeof(sum));
        for(int i=1; i<=n; i++)scanf("%lld",&sum[i]);
        sum[n+1]=er;
        sort(sum+1,sum+n+2);
        int l=1,r=er;
        while(l<r)
        {
            int mid=(l+r)>>1;
            int cnt=0;
            for(int i=1,j=0; i<=n+1; i++)
                if(sum[i]-sum[j]<=mid)
                    cnt++;
                else j=i;
            if(cnt>co)r=mid;//右推
            else l=mid+1;
     
        }
        cout << l << endl;
    }

}

Before the invention of book-printing, it was very hard to make a copy
of a book. All the contents had to be re-written by hand by so called
scribers. The scriber had been given a book and after several months
he finished its copy. One of the most famous scribers lived in the
15th century and his name was Xaverius Endricus Remius Ontius
Xendrianus (Xerox). Anyway, the work was very annoying and boring. And
the only way to speed it up was to hire more scribers.

Once upon a time, there was a theater ensemble that wanted to play
famous Antique Tragedies. The scripts of these plays were divided into
many books and actors needed more copies of them, of course. So they
hired many scribers to make copies of these books. Imagine you have m
books (numbered 1, 2 … m) that may have different number of pages
(p1, p2 … pm) and you want to make one copy of each of them. Your
task is to divide these books among k scribes, k <= m. Each book can
be assigned to a single scriber only, and every scriber must get a
continuous sequence of books. That means, there exists an increasing
succession of numbers 0 = b0 < b1 < b2, … < b k-1 <= bk = m such
that i-th scriber gets a sequence of books with numbers between bi-1+1
and bi. The time needed to make a copy of all the books is determined
by the scriber who was assigned the most work. Therefore, our goal is
to minimize the maximum number of pages assigned to a single scriber.
Your task is to find the optimal assignment. Input The input consists
of N cases. The first line of the input contains only positive integer
N. Then follow the cases. Each case consists of exactly two lines. At
the first line, there are two integers m and k, 1 <= k <= m <= 500. At
the second line, there are integers p1, p2, … pm separated by
spaces. All these values are positive and less than 10000000. Output
For each case, print exactly one line. The line must contain the input
succession p1, p2, … pm divided into exactly k parts such that the
maximum sum of a single part should be as small as possible. Use the
slash character (’/’) to separate the parts. There must be exactly one
space character between any two successive numbers and between the
number and the slash.

If there is more than one solution, print the one that minimizes the
work assigned to the first scriber, then to the second scriber etc.
But each scriber must be assigned at least one book.

题目大意:有一些书,连续分给一些人去抄写,每个人至少分得1份,求最小分给单个人抄写的最大页数,并用‘/‘分割输出。
枚举最小单个人抄写最大页数,最小为max(p1……pn)最大为页数综合,用二分法得出最佳解m。然后从后向前找出’/'的位置(>m)。如果找出来的位置数小于k-1,再从前面找非分割点填补。

ll m,n,book[501],vis[500];
inline void scan_f(ll &x)
{
    x=0;
    char in=getchar();
    while(in>'9'||in<'0')
        in=getchar();
    while(in<='9'&&in>='0')
    {
        x=(x<<3)+(x<<1)+in-'0';
        in=getchar();
    }
}
ll judge(ll x)
{
    ll sum=0,j=1;
    for(int i=m; i>=1; i--)
    {
        if(sum+book[i]>x)
        {
            j++;
            sum=book[i];
        }
        else
            sum+=book[i];
    }
    return j<=n;
}
int main()
{
    ll op;
    cin>>op;
    while(op--)
    {
        ll sum1=0;
        memset(vis,0,sizeof(vis));
        scan_f(m);
        scan_f(n);    ll l=0;
        for(int i=1; i<=m; i++)
        {
            scan_f(book[i]);
            l=max(l,book[i]);
            sum1+=book[i];
        }

        ll r=sum1;
        while(l<r)
        {
            ll mid=(l+r)>>1;
            if(judge(mid))
                r=mid;//左推
            else
                l=mid+1;
        }
        ll b=r,total=0,k=0;
        for(int i=m; i>=1; i--)
        {
            total+=book[i];
            if(total>b)
            {
                total=0;
                vis[i]=1;
                i++;
                k++;
            }
        }
        k=n-k-1;
        ll yu=0;
        while(k)//判断是否达到n-1个分割点
        {
            if(!vis[++yu])
            {
                vis[yu]=1;//设置分割点
                k--;
            }
        }
        for(int i=1; i<=m-1; i++)
        {
            cout<<book[i]<<" ";
            if(vis[i])
                cout<<"/ ";
        }
        cout<<book[m];//单独输出
        cout<<endl;
        sum1=0;
    }
    return 0;
}

P

We all know the impressive story of Robin Hood. Robin Hood uses his
archery skills and his wits to steal the money from rich, and return
it to the poor.

There are n citizens in Kekoland, each person has c i coins. Each day,
Robin Hood will take exactly 1 coin from the richest person in the
city and he will give it to the poorest person (poorest person right
after taking richest’s 1 coin). In case the choice is not unique, he
will select one among them at random. Sadly, Robin Hood is old and
want to retire in k days. He decided to spend these last days with
helping poor people.

After taking his money are taken by Robin Hood richest person may
become poorest person as well, and it might even happen that Robin
Hood will give his money back. For example if all people have same
number of coins, then next day they will have same number of coins
too.

Your task is to find the difference between richest and poorest
persons wealth after k days. Note that the choosing at random among
richest and poorest doesn’t affect the answer.

Input The first line of the input contains two integers n and k
(1 ≤ n ≤ 500 000, 0 ≤ k ≤ 109) — the number of citizens in Kekoland
and the number of days left till Robin Hood’s retirement.

The second line contains n integers, the i-th of them is c i (1 ≤ c
i ≤ 109) — initial wealth of the i-th person.

Output Print a single line containing the difference between richest
and poorest peoples wealth.

题意:在一个城市中有n个人每个人有不同的金币数,罗宾汉,每天将一枚金币从最富的人手中偷走并交给最穷的人,但是他还有m天便会不干了,求在m天后,最富的人与最穷的人金币数差。
根据题意知金币数从最大的一端,向最小的一端转移,所以两端的金币数都趋近于sum/n,这样可以确定左端,与右端的范围。金币转移数为(最终金币数-小于它的人的金币总数),或(大于它的金币总数-最终金币数)。据题知,每天只转移一枚金币。由此可根据天数用二分法推测最终金币数。存在一个特殊条件,当m%n!=0时左右段不可能趋近为一点,两者最小相差为一。在这条件下我们可以缩小右端的范围为(sum/n+1,sum)保证结果的正确。最后右端趋近值减左端趋近值为结果。

inline bool check1(ll m){
	ll res=0;
	for(int i=1;i<=n;i++){
		if(a[i]<m){
			res+=m-a[i];
		}
	}
	return res<=k;
}
inline bool check2(ll m){
	ll res=0;
	for(int i=1;i<=n;i++){
		if(a[i]>m){
			res+=a[i]-m;
		}
	}
	return res<=k;
}
int main(){
	n=read();k=read();
	for(int i=1;i<=n;i++) a[i]=read(),sum+=a[i];
	ll l=1,r=sum/n,mid,ans1=0,ans2=0;
	while(l<=r){
		mid=l+r>>1;
		if(check1(mid)){
			ans1=mid;//右推
			l=mid+1;
		}
		else{
			r=mid-1;
		}
	}
	l=sum/n,r=sum;
	if(sum%n) l++;
	while(l<=r){
		mid=l+r>>1;
		if(check2(mid)){
			ans2=mid;//
			r=mid-1;
		}
		else{
			l=mid+1;
		}
	}
	printf("%d\n",ans2-ans1);
	return 0;

总结:这周学习了二分,感觉二分法的边界查找挺复杂的,左侧边界查找,右侧边界查找,还有一个基本的查找,看了许多博客才开始明白。练习也有许多不会的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值