2021年暑期训练阶段三Day3

目录

A - 敌兵布阵

B - Sum of Consecutive Prime Numbers

C - 天上的星星

D - Graveyard Design

E - Exam in BerSU (hard version)

F - Maximum Sum

G - Pie


A - 敌兵布阵

题意:

C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视。
中央情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工兵营地一共有多少人,例如Derek问:“Tidy,马上汇报第3个营地到第10个营地共有多少人!”Tidy就要马上开始计算这一段的总人数并汇报。但敌兵营地的人数经常变动,而Derek每次询问的段都不一样,所以Tidy不得不每次都一个一个营地的去数,很快就精疲力尽了,Derek对Tidy的计算速度越来越不满:"你个死肥仔,算得这么慢,我炒你鱿鱼!”Tidy想:“你自己来算算看,这可真是一项累人的工作!我恨不得你炒我鱿鱼呢!”无奈之下,Tidy只好打电话向计算机专家Windbreaker求救,Windbreaker说:“死肥仔,叫你平时做多点acm题和看多点算法书,现在尝到苦果了吧!”Tidy说:"我知错了。。。"但Windbreaker已经挂掉电话了。Tidy很苦恼,这么算他真的会崩溃的,聪明的读者,你能写个程序帮他完成这项工作吗?不过如果你的程序效率不够高的话,Tidy还是会受到Derek的责骂的.

思路:

线段树,百度是个好东西

线段树详解 - Xenny - 博客园 (cnblogs.com)

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;

struct node
{
    int l,r,num;
}s[500000];

int h[500000];

int creat(int t,int l,int r)//建树
{
    if(l==r)
    {
        s[t].l=l;
        s[t].r=r;
        return s[t].num=h[l];
    }
    s[t].l=l;
    s[t].r=r;
    return s[t].num=creat(t<<1,l,(l+r)>>1)+creat((t*2)+1,((l+r)/2)+1,r);

}

void addre(int t,int a,int b)//单点更新
{

    if(a==s[t].l&&a==s[t].r)
    {
        s[t].num=s[t].num+b;
        return ;
    }
    if(((s[t].l+s[t].r)>>1)>=a)
    {
        addre(t*2,a,b);
    }
    else
    {
        addre(t*2+1,a,b);
    }
    s[t].num=s[t<<1].num+s[t*2+1].num;
}

int query(int t,int l,int r)//区间查询
{
    int mid=(s[t].l+s[t].r)>>1;
    if(s[t].l==l&&s[t].r==r)
    {
        return s[t].num;
    }
    if(mid>=r)
    {
        return query(t*2,l,r);
    }
    else if(mid<l)
    {
        return query(t*2+1,l,r);
    }
    else
    {
        return query(t*2,l,mid)+query(t*2+1,mid+1,r);
    }
}

int main()
{
    int T;
    int n;
    int a,b,mm=0;
    char m[20];
    scanf("%d",&T);
    while(T--)
    {
        ++mm;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&h[i]);
        }
        creat(1,1,n);
        printf("Case %d:\n",mm);
        while(~scanf("%s",m))
        {
            if(strcmp(m,"End")==0) break;
            if(strcmp(m,"Add")==0)
            {
                scanf("%d %d",&a,&b);
                addre(1,a,b);
            }
            if(strcmp(m,"Sub")==0)
            {
                scanf("%d %d",&a,&b);
                addre(1,a,-b);
            }
            if(strcmp(m,"Query")==0)
            {
                scanf("%d %d",&a,&b);
                printf("%d\n",query(1,a,b));
            }
        }
    }
    return 0;
}

B - Sum of Consecutive Prime Numbers

题意:

一些正整数可以用一个或多个连续素数的和表示。一个给定的正整数有多少这样的表示?例如,整数53有两种表示形式5+7+11+13+17和53。整数41有三种表示形式2+3+5+7+11+13、11+13+17和41。整数3只有一种表示形式,即3。整数20没有这种表示形式。请注意,总和必须是连续素数

数字,因此7+13和3+5+5+7都不是整数20的有效表示形式。

您的任务是编写一个程序,报告给定正整数的表示数。

思路:

如题意,都应是素数,所以先做一次素数筛,然后再尺取答案

代码:

#include<iostream>
#include<cstdio>
#include<set>
#include<map>
#include<algorithm>
using namespace std;

#define maxn 10000000
bool vis[maxn];
int prime[maxn],x;

void oulashai(int n)  //欧拉筛
{
    for(int i=2; i<=n; i++)
    {
        if(!vis[i])
            prime[x++]=i;
        for(int j=0; j<x; j++)
        {
            if(i*prime[j]>n)
                break;
            vis[i*prime[j]]=true;
            if(i%prime[j]==0)
                break;
        }
    }
}

int main()
{

    oulashai(100005);
    int n;
    while(cin>>n&&n)
    {
        int l=0,r=0,sum=0,num=0;
        while(true)
        {
            while(prime[r]<=n&&sum<n)
                sum+=prime[r++];
            if(sum==n)
                num++;
            sum-=prime[l++];
            if(sum<0)
                break;
        }
        cout<<num<<endl;
    }
    return 0;
}

C - 天上的星星

题意:

在一个星光摧残的夜晚,蒜头君一颗一颗的数这天上的星星。

蒜头君给在天上巧妙的画了一个直角坐标系,让所有的星星都分布在第一象。天上有 n$颗星星,他能知道每一颗星星的坐标和亮度。

现在,蒜头君问自己 q次,每次他问自己每个矩形区域的星星的亮度和是多少(包含边界上的星星)。

思路:

求平面区域内和,二维前缀和和差分

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
int w[2005][2005];//w[i][j]表示i,j位置处星星的亮度(差分数组)
int sum[2005][2005];//sum[i][j]表示从1,1到i,j矩形区域内w数组的和(二维前缀和数组)
int main()
{
    int n;
    cin >> n;
    for(int i = 0; i < n; i++)
    {
        int x, y;
        scanf("%d %d", &x, &y);//星星的坐标为x,y
        int L;
        scanf("%d", &L);
        y++;//将区域(0,0)-(200000,200000)转移为(1,1)-(200001,200001),更易于求前缀和数组sum
        x++;
        w[y][x] += L;//x为列坐标,y为横坐标
    }
    for(int i = 1; i < 2002; i++)//求w的前缀和数组sum
    {
        for(int j = 1; j < 2002; j++)//都从1开始,w[0][x]与w[x][0]都为零,这样求的时候不用考虑数组越界
        {
            sum[i][j] = w[i][j] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];//即i,j>=1 i-1,j-1>=0
        }
    }
    int q;
    cin >> q;
    for(int i = 0; i < q; i++)
    {
        int x1, y1, x2, y2;
        scanf("%d %d %d %d", &x1, &y1, &x2, &y2);//询问q次
        x1++;//转换为(1,1)到(200001,200001)的坐标
        y1++;
        x2++;
        y2++;
        int ans = sum[y2][x2]-sum[y2][x1-1]-sum[y1-1][x2]+sum[y1-1][x1-1];//通过二维前缀和求二维区间内的和
        cout << ans << endl;
    }
    return 0;
}

D - Graveyard Design

题意:

乔治国王最近决定为皇家墓地设计一个新的设计。墓地必须由几个部分组成,每个部分必须是一个正方形的坟墓。所有路段必须有不同数量的坟墓。

在咨询占星家后,乔治国王决定截面边的长度必须是连续的正整数序列。边长为s的剖面包含s2个坟墓。乔治估计了墓地上的坟墓总数,现在他想知道所有可能满足条件的墓地设计。你被要求找到他们。

思路:

在所有墓地中查找符合条件的墓地,查找问题,尺取法

代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#define INF 10000005
using namespace std;
typedef long long ll;
struct node
{
    ll answer,l,r;//这里的answer是区间长度,l是左端点,r是右端点
} ans[INF];
bool cmp(node n1,node n2)
{
    return n1.answer>n2.answer;
}
int main()
{
    ll n;
    while(~scanf("%lld",&n))
    {
        ll left=1,right=1,sum=0,x=0;
        ll s=sqrt(n);
        while(left<=s)//左端点在s范围内
        {
            while(sum<n&&right-1<=s)//这里是右端点在s范围内
            {
                sum+=right*right;
                right++;//因为这里每次都要跑一次right++,所以实际sum的区间长度是[left,right-1]
            }
            if(sum==n)
            {
                ans[x].answer=right-1-left+1;//右端点-左端点-1是区间长度
                ans[x].l=left;//左端点
                ans[x].r=right-1;//右端点
                x++;
                sum+=right*right;//这儿呢必须要有这个,就是说sum=n之后,判断之后的还有没有序列满足条件,
                                 //就直接再加下一个数,然后sum就会大于n了,再接着跑下边那一个if
                                 //(再说一下就是这儿的right已经是区间右端点的下一个数了,所以直接加right就ok)
                right++;//保证right一直是区间右端点的右边一个数
            }
            if(sum>n)
            {
                sum-=left*left;
                left++;
            }
        }
        sort(ans,ans+x,cmp);
        printf("%lld\n",x);
        for(ll i=0; i<x; i++)
        {
            printf("%lld ",ans[i].answer);
            for(ll j=ans[i].l; j<=ans[i].r; j++)
                printf("%lld%c",j," \n"[j==ans[i].r]);
        }
    }
    return 0;
}

// for(long long i=1;i<=7;i++)
//          printf("%lld%c",i," \n"[i==7]);  \n换成.看看
//    cout<<1;

E - Exam in BerSU (hard version)

题意:

Beland州立大学已经开始了一个课程。许多学生正在参加考试。

Poligrafovich测谎仪将对一群n名学生进行检查。学生将从第1次到第n次依次参加考试。考试规则如下:

第i个学生随机选择一张票。

如果这张罚单对学生来说太难了,他不会回答,而是立即回家(这个过程太快了,以至于没有时间流逝)。这个学生考试不及格。

如果学生觉得这张票容易,他会花整整一分钟通过考试。比赛结束后,他立即得到分数并回家。

学生们按照固定的顺序,一个接一个地参加考试,没有任何中断。在任何时候,Poligrafovich测谎仪都会从一个学生那里得到答案。

对于所有学生来说,整个考试的持续时间为M分钟(最大),因此列表末尾的学生更有可能没有时间通过考试。

对于每个i学生,你应该计算需要考试不及格的学生的最小可能数量,以便第i个学生有足够的时间通过考试。

对于每个学生,我都会独立地找到答案。也就是说,如果在为学生i_1找到答案时,一些学生j应该离开,那么在为i_2找到答案时(i_2>i_1),学生j不必回家。

思路:

记录每个数字出现的次数,对于每个数字只需要从左往右扫一遍这个数组,
将较小的数字先取了,直到不能取为止。

代码:

#include<iostream>
using namespace std;
int a[105];
//记录每个数字出现的次数,对于每个数字只需要从左往右扫一遍这个数组,
//将较小的数字先取了,直到不能取为止。
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int t;
        cin>>t;
        int man=0;  //人
        int remain=m-t; //剩下可以让他之前的人考试的时间
        for(int j=1;j<=100;j++)  //从时间少的开始选
        {
            int x=min(remain/j,a[j]); //总时间和每个时间的次数限制下的最多可考数
            man+=x;
            remain-=x*j;
        }
        a[t]++;
        cout<<i-1-man<<" ";
    }
}

F - Maximum Sum

题意:

给定一个正整数和负整数的二维数组,找到具有最大值的子矩形

总和矩形的和是该矩形中所有元素的和。在这个问题中,和最大的子矩形被称为最大子矩形。

子矩形是位于整个数组中大小为1×1或更大的任何连续子数组。

思路:

给出n*n的矩阵,求出里面子矩阵的和的最大值。

序列是一维的,矩阵是二维的,所以我们可以把矩阵转换为一维的来算。

也就是枚举矩阵的连续几行的合并,这样就转换为一维的了,再找其最大子序列,更新最大值就可以了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
int num[110][110];
int main()
{
    int n;
    while(~scanf("%d",&n)&&n)
    {
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                scanf("%d",&num[i][j]);
            }
        }
        int maxx=num[0][0];
        for(int i=1; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                num[i][j]+=num[i-1][j];//把上下两个数之和当成一个数
            }
        }
        for(int i=0; i<n; i++)
        {
            for(int j=i; j<n; j++)
            {
                int sum=0;
                for(int k=0; k<n; k++)
                {
                    if(sum<0)//当出现小于0的和直接放弃
                        sum=0;
                    else if(i!=j)
                        sum+=num[j][k]-num[i][k];//矩阵的正数之和
                    if(sum>maxx&&sum>0)//比较正数中比较大的那个
                        maxx=sum;
                }
            }
        }
        printf("%d\n",maxx);
    }
}

G - Pie

题意:

我的生日快到了,传统上我要做馅饼。不只是一个馅饼,不,我有N个馅饼,各种口味,各种尺寸。我的几个朋友要来参加我的聚会,他们每人都得到一块馅饼。这应该是一个馅饼的一块,而不是几个小块,因为看起来很乱。不过这一块可以是一整块馅饼。

我的朋友们很烦人,如果他们中的一个比其他人得到更大的一块,他们就会开始抱怨。因此,所有的馅饼都应该得到同样大小(但不一定形状相同)的馅饼,即使这会导致一些馅饼变质(这比破坏聚会要好)。当然,我自己也想要一块馅饼,而且那块馅饼的大小也应该一样。

我们所有人能得到的最大尺寸是多少?所有的馅饼都是圆柱形的,它们都有相同的高度1,但是馅饼的半径可以不同。

思路:

求出每个馅饼体积,求和除以人数求出最大半径,之后不断二分找出合适的馅饼半径

注意,人数要+1(自己)

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
using namespace std;
const double pi=acos(-1.0);
double a[100005];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,f,r,sum1;
        double sum=0,minn,maxx,mid;
        scanf("%d%d",&n,&f);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&r);
            a[i]=r*r*pi*1.0; //每个馅饼体积
            sum+=a[i];
        }
        minn=0; //下界
        maxx=sum/(f+1); //上界
        while(maxx-minn>1e-5) //二分
        {
            mid=minn+(maxx-minn)/2; //每个人分到的馅饼半径
            sum1=0;
            for(int i=0;i<n;i++)
                sum1+=(int)(a[i]/mid); //每个馅饼按mid半径分后的总人数
            if(sum1>=(f+1)) //更新边界
                minn=mid;
            else
                maxx=mid;
        }
        printf("%.4lf\n",mid);
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视。 中央情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工兵营地一共有多少人,例如Derek问:“Tidy,马上汇报第3个营地到第10个营地共有多少人!”Tidy就要马上开始计算这一段的总人数并汇报。但敌兵营地的人数经常变动,而Derek每次询问的段都不一样,所以Tidy不得不每次都一个一个营地的去数,很快就精疲力尽了,Derek对Tidy的计算速度越来越不满:"你个死肥仔,算得这么慢,我炒你鱿鱼!”Tidy想:“你自己来算算看,这可真是一项累人的工作!我恨不得你炒我鱿鱼呢!”无奈之下,Tidy只好打电话向计算机专家Windbreaker求救,Windbreaker说:“死肥仔,叫你平时做多点acm题和看多点算法书,现在尝到苦果了吧!”Tidy说:"我知错了。。。"但Windbreaker已经挂掉电话了。Tidy很苦恼,这么算他真的会崩溃的,聪明的读者,你能写个程序帮他完成这项工作吗?不过如果你的程序效率不够高的话,Tidy还是会受到Derek的责骂的.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值