swpu2016级第一周寒假作业题解

这是16级同学第一周寒假作业的题解,内容大概是二分,双指针和预处理优化,题解不会很详细,大家要自己先去看题才行,给出题目连接:连接在这里


A.考察一下大家具体的二分水平,大于等于,大于,小于等一系列应用,用STL的二分可以很轻松解决:

#include <bits/stdc++.h>

using namespace std;

int main(){
    int n,q,a[100005],c,x,y;
    scanf("%d",&n);
    for(int i = 1;i <= n;i++)
        scanf("%d",&a[i]);
    scanf("%d",&q);
    for(int i = 0;i < q;i++){
        scanf("%d",&c);
        if(c == 0){
            scanf("%d",&x);
            if(x < a[1]||x > a[n]||a[lower_bound(a+1,a+1+n,x)-a]!=x) puts("-1");
            else printf("%d\n",upper_bound(a+1,a+1+n,x)-a-2);
        }
        else if(c == 1){
            scanf("%d",&x);
            if(x < a[1]||x > a[n]||a[lower_bound(a+1,a+1+n,x)-a]!=x) puts("-1");
            else printf("%d\n",upper_bound(a+1,a+1+n,x-1)-a-1);
        }
        else if(c == 2){
            scanf("%d%d",&x,&y);
            printf("%d\n",upper_bound(a+1,a+1+n,y)-a-(upper_bound(a+1,a+1+n,x-1)-a));
        }
        else if(c == 3){
            scanf("%d",&x);
            if(x >= a[n]) puts("-1");
            else printf("%d\n",a[lower_bound(a+1,a+1+n,x+1)-a]);
        }
        else{
            scanf("%d",&x);
            if(x <= a[1]) puts("-1");
            else printf("%d\n",a[lower_bound(a+1,a+1+n,x)-a-1]);
        }
    }
    return 0;
}


B.浮点二分的查询,用一个数组保存到当前位置的下标之和,然后重载一下运算符,然后二分查找一下,由于是连续区间,等差数列求和即可得到正确答案
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 1e5+5;

ll sum[maxn];

struct Node
{
    double x,y;
}a[maxn];

bool operator <(Node a,Node b)
{
    if(a.x == b.x) return a.y < b.y;
    return a.x < b.x;
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        for (int i=0;i<n;i++)
            scanf("%lf%lf",&a[i].x,&a[i].y);
        for (int i = 0;i < n;i++)
            sum[i] = n-1-i;
        sort(a,a+n);
        for(int i = 0;i < m;i++)
        {
            Node lll,rrr;
            scanf("%lf%lf%lf%lf",&rrr.x,&rrr.y,&lll.x,&lll.y);
            ll llll = lower_bound(a,a+n,lll)-a;
            ll rrrr = upper_bound(a,a+n,rrr)-a-1;
            printf("%I64d\n",(sum[llll] + sum[rrrr])*(rrrr-llll+1)/2);
        }
    }
    return 0;
}


C.给定n个有长度绳子和要求得到长度一样绳子的个数,求最大长度,因为是求精确到厘米,可以乘上100转变为整数二分,最大的可能长度是最长绳子的长度而不是最短绳子的长度,这点需要注意,用绳子长度整除二分之后的长度即为可以得到的数量,如果超过需要的数量,因为需要求最大长度,所以继续在右半区间二分,小于在左边区间二分,注意这里double的输入输出表示即可,别忘了算出结果后除100,还有,这题是在poj上测的,所以不能用bits/stdc++.h这个头文件:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define N 10010
#define MAX 10000000

int a[N];

int main()
{
    int n,m;
    double len;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int Max = 0;
        for(int i=0; i<n; i++)
        {
            scanf("%lf",&len);
            a[i] = len * 100;
            Max = max(Max , a[i]);
        }
        int low = 1 , high = Max;
        int res = 0;
        while(low <= high)
        {
            int mid = (low + high) >> 1;
            int count = 0;
            for(int i=0; i<n; i++)
                count += a[i] / mid;

            if(count >= m)
                res = max(res , mid) , low = mid + 1;
            else
                high = mid - 1;
        }
        printf("%.2f\n",(double)res / 100.0);
    }
    return 0;
}


D.我们可以用数组保存每一次相遇时所需要的时间,然后二分查询即可,注意精度问题,因为涉及到除法,所以每个相遇时间都减少一个min值,如1e-6,这样不会出现临界情况,因为用upper_lower计算的是第一个大于此数的下标,所以减一下保证刚好相等的情况不会跳到下一个数
#include <bits/stdc++.h>

using namespace std;

int main(){
    double l,va,vc,k,a[100100];
    int q;
    while(~scanf("%lf%lf%lf",&l,&va,&vc)){
        a[0] = 0;
        for(int i = 1;i <= 10000;i++)
            a[i] = a[i-1]+i-1+(l/(va+vc)-1e-6);
        scanf("%d",&q);
        while(q--){
            scanf("%lf",&k);
            printf("%d\n",upper_bound(a,a+10001,k)-a-1);
        }
    }
    return 0;
}


E.由于是头尾相连,所以可以先双指针扫描前n个数,然后再扫描前n-1个数,相等的时候就累加一下,需要注意的是因为数组长度就n,所以左右指针的间距不能超过n,这种双指针写法是Home_W发明的,很不错的写法,大家可以去学习:
#include <bits/stdc++.h>

using namespace std;
const int maxn = 1e6+10;

int n,m,a[maxn];

int main(){
    while(~scanf("%d%d",&n,&m)){
        int ans,sum,bot;
        ans = sum = bot = 0;
        for(int i = 0;i < n;i++){
            scanf("%d",&a[i]);
            sum += a[i];
            while(sum > m) sum-=a[bot++];
            if(sum == m) ans++;
        }
        if(bot)
        for(int i = 0;i < n-1;i++){
            sum += a[i];
            while(sum > m) sum -= a[bot++];
            if(bot >= n) break;
            if(sum == m) ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}


F.求最小长度的序列和大与等于S的长度,仍然是双指针扫描,具体的看代码吧

#include <stdio.h>
#define MAX 100100
int main()
{
	int T,N,S;
	int i=0,data[100100],min,head=0,rear=0,sum=0;
	scanf("%d",&T);
	while(T--)
	{
		min=MAX;
		scanf("%d%d",&N,&S);
		for(i=0;i<N;++i)
		{
			scanf("%d",&data[i]);
		}
		head=0;
		rear=1;
		sum=data[0];
		while(rear < N)
		{
			while(rear < N&&sum < S)
			{
				sum+=data[rear++];
			}
			sum-=data[head++];
			while(sum>=S)
			{
				sum-=data[head++];
			}
			if (sum+data[head-1]>=S && rear-head+1<min)
			{
				min=rear-head+1;
			}
		}
		if(MAX == min)
		{
			puts("0");
		}
		else
		{
			printf("%d\n",min);
		}
	}
}


G.因为n的数据够小,预处理一下前缀和,n^2也是可以卡过这道题目的:
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int main(){
    ll a[10005],sum[10005],m;
    int n,q,t;
    scanf("%d",&t);
    while(t--){
        memset(a,0,sizeof(a));
        memset(sum,0,sizeof(sum));
        scanf("%d%d",&n,&q);
        for(int i = 1;i <= n;i++)
            scanf("%lld",&a[i]),sum[i] = sum[i-1]+a[i];
        while(q--){
            int cot = 0;
            scanf("%lld",&m);
            for(int i = 0;i < n;i++){
                for(int j = i+1;j <= n;j++){
                    if(sum[j] - sum[i] == m)
                        cot++;
                }
            }
            printf("%d\n",cot);
        }
    }
    return 0;
}

H.注意一下,这里的数全部都是正整数,所以双指针扫描一下,如果出现等于的数,加一下,扩大右端点,如果大于需要的数,缩进左端点即可

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

ll a[10006];

int main(){
    int n,q,i,bot,ans,t;
    ll sum,m;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&q);
        for(i = 0;i < n;i++) scanf("%lld",&a[i]);
        while(q--){
            scanf("%lld",&m);
            for(sum = bot = i = ans = 0;i < n;i++){
                sum += a[i];
                while(sum > m) sum -= a[bot++];
                if(sum == m) ans++;
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

I.因为需要查询的数在-1e6到1e6的区间里,所以可以开一个数组保存我们需要的数有多少个,可以把要查询的数加上一个1e6使得查询的数为非负数,为开数组做处理,查询的时候也加一个1e6即可,防止数组越界,判断那一段连续区间和的数是否在我们想要的区间里,不在就不管,因为题目的数据保证不会出现超过1e6或者小于-1e6的数

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 1e6;

int t,n,q,Q,c;
int a[maxn],sum[maxn],ans[2000005];

int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&q);
        memset(sum,0,sizeof(sum));
        sum[0] = 0;
        for(int i = 0;i < n;i++){
            scanf("%d",&a[i]);
            sum[i+1] = sum[i] + a[i];
        }
        memset(ans,0,sizeof(ans));
        for(int i = 0;i < n;i++){
            for(int j = i;j < n;j++){
                c = sum[j+1]-sum[i]+maxn;
                if(c >= 0&&c <= 2*maxn)
                    ans[c]++;
            }
        }
        for(int i = 0;i < q;i++){
            scanf("%d",&Q);
            printf("%d\n",ans[Q+maxn]);
        }
    }
    return 0;
}

J.认真看过这周算法讲堂的同学一定会做这道题目(点我去看视频啦),双指针的最好范例,具体解法自己去看视频
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 1e6+10;

int t,n,m,l,r,w,s,x,y;
int a[maxn],sum[maxn];

int main(){
    scanf("%d",&t);
    while(t--){
        memset(a,0,sizeof(a));
        memset(sum,0,sizeof(sum));
        scanf("%d%d%d%d",&n,&m,&s,&w);
        while(m--){
            scanf("%d%d",&x,&y);
            a[x]++;a[y+1]--;
        }
        for(int i = 1;i <= n;i++)
            a[i] = a[i-1] + a[i];
        for(int i  = 1;i <= n;i++)
            sum[i] = sum[i-1] + a[i];
        ll ans = 0,l = 1,r = 1;
        for(int i = 1;i <= n;i++){
            while(r < i&&sum[i] - sum[r] >= w)
                ++r;
            while(l < r-1&&sum[i] - sum[l] >= s&&
                  sum[r-1]-sum[l] >= w)
                ++l;
            if(l < r&&sum[i]-sum[l-1] >= s&&sum[i]-
               sum[r-1] >= w&&sum[r-1]-sum[l-1]>=w)
                ans += l;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


题目真多,写题解写的累死了,大家加油啊,另外加了个寒假训练的记分机制,跟寒假回来的分队是会挂钩的,具体情况点这里,看最下面写的ps:怒点这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值