[补档]noip2019集训测试赛(十五)

Problem A: 传送带

Time Limit: 1000 ms Memory Limit: 256 MB

Description

在一个二维平面上有两条传送带,每一条传送带可以看成是一条线段。两条传送带分别为线段AB和线段CD。小y在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R。现在,小y想从A点走到D点,请问他最少需要走多长时间。

Input

第一行是4个整数,表示A和B的坐标,分别为Ax,Ay,Bx,By。

第二行是4个整数,表示C和D的坐标,分别为Cx,Cy,Dx,Dy。

第三行是3个整数,分别是P,Q,R。

Output

输出一行一个数,表示小y从A点走到D点的最短时间,保留到小数点后2位。

Sample Input
0 0 0 100
100 0 100 100
2 2 1
Sample Output
136.60

HINT

对于30%的数据满足:

1<=Ax,Ay,Bx,By,Cx,Cy,Dx,Dy<=10

1<=P,Q,R<=5

对于100%的数据满足:

1<=Ax,Ay,Bx,By,Cx,Cy,Dx,Dy<=1000

1<=P,Q,R<=10

Solution

三分板子题?

首先三分起始点与A的距离,然后再套一个三分算出从起始点出发到达CD上某一点的最小距离即可。

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-4;
struct point{
    double x,y;
    point(){}
    point(double xx,double yy){x=xx,y=yy;}
}A,B,C,D;
double AB,CD;
double p,q,r;
point operator +(point a,point b){
    return point(a.x+b.x,a.y+b.y);
}
point operator -(point a,point b){
    return point(a.x-b.x,a.y-b.y);
}
point operator *(point a,double k){
    return point(a.x*k,a.y*k);
}
double dis(point a,point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double cal(point X,double x){
    point tmp=C;
    if(CD!=0.0){
        tmp=(C+(D-C)*(x/CD));
    }
    return dis(X,tmp)/r+(CD-x)/q;
}
double calc(double x){
    point tmp=A;
    if(AB!=0.0){
        tmp=(A+(B-A)*(x/AB));
    }
    double l=0,r=CD;
    while(r-l>eps){
        double mid=(l+r)/2.0;
        double m1=(mid-eps/2.0),m2=(mid+eps/2.0);
        if(cal(tmp,m1)<=cal(tmp,m2)){
            r=mid;
        }
        else l=mid;
    }
    return x/p+cal(tmp,(l+r)/2.0);
}
int main(){
    scanf("%lf%lf%lf%lf",&A.x,&A.y,&B.x,&B.y);
    scanf("%lf%lf%lf%lf",&C.x,&C.y,&D.x,&D.y);
    scanf("%lf%lf%lf",&p,&q,&r);
    AB=dis(A,B),CD=dis(C,D);
    double l=0,r=AB;
    while(r-l>eps){
        double mid=(l+r)/2.0;
        double m1=(mid-eps/2.0),m2=(mid+eps/2.0);
        //cout<<calc(m1)<<" "<<calc(m2)<<endl;
        if(calc(m1)<=calc(m2)){
            r=mid;
        }
        else l=mid;
    }
    //printf("%.2lf\n",(l+r)/2.0);
    printf("%.2lf\n",calc((l+r)/2.0));
}

Problem B: 疯狂的火神

Time Limit: 1000 ms Memory Limit: 256 MB

Description

火神为了检验zone的力量,他决定单挑n个人。

由于火神训练时间有限,最多只有t分钟,所以他可以选择一部分人来单挑,由于有小y的帮助,他得到了每个人特定的价值,每个人的价值由一个三元组(a,b,c)组成,表示如果火神在第x分钟单挑这个人,他就会得到a-b*x的经验值,并且他需要c分钟来打倒这个人。

现在火神想知道,他最多可以得到多少经验值,由于火神本来就很笨,进入zone的疯狂的火神就更笨了,所以他希望你来帮他计算出他最多可以得到多少经验值。

Input

第一行一个正整数T,表示数据组数。

对于每组数据,第一行为两个正整数n和t,表示跟火神单挑的人的个数和火神的训练时间。下面n行,每行三个正整数Ai,Bi,Ci,表示每个人的价值,含义见题目。

Output

对于每组数据输出一行一个整数,表示火神最多能得到多少经验值。

Sample Input
1
4 10
110 5 9
30 2 1
80 4 8
50 3 2
Sample Output
88

HINT

对于20%的数据满足:1≤n≤10

对于50%的数据满足:1≤n≤18

对于100%的数据满足:1≤n≤1000,1≤t≤3000,1≤Ci≤t,Ai≤10^6

保证n>200的数据组数不超过5组,其他的数据组数不超过10组

保证每个人贡献的经验值到训练结束都不会变成负数

Solution

恕我直言题目描述就是一坨shit!

他的经验值获取的计算是在击败后才算的。

考虑对于当前某一时间下怎样选才能最大化自己所获得的经验。

因为经验会随着时间流逝,所以对于任意一个选择的组合,一定是减的比较多的优先选择。

证明:设两个人他们的b值和c值分别为\(b1,c1,b2,c2\),且有\({b1 \over c1} > {b2 \over c2}\)

则如果让1在前面,所失去的经验是\(b1 \times c1+b2 \times (c1+c2)\),否则就是\(b2 \times c2+b1 \times (c1+c2)\)

两边都减去\(b1 \times c1+b2 \times c2\),留下来的就是\(b2 \times c1\)和$ b1 \times c2$

结合最上面的式子,可以得到\(b2 \times c1 < b1 \times c2\)

所以我们要让1排在前面。

于是我们按照c/b排序然后直接单次背包DP即可。

#include<bits/stdc++.h>
using namespace std;
struct sb{
    int a,b,c;
}a[200001];
bool operator <(sb x,sb y){
    return x.c*1ll*y.b<x.b*1ll*y.c;
}
int dp[200001];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        memset(dp,0,sizeof(dp));
        int n,t;
        scanf("%d%d",&n,&t);
        for(int i=1;i<=n;++i){
            scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c);
        }
        sort(a+1,a+1+n);
        //for(int i=1;i<=n;++i){
            //cout<<a[i].a<<" "<<a[i].b<<" "<<a[i].c<<endl;
        //}
        for(int i=1;i<=n;++i){
            for(int j=t-a[i].c;~j;--j){
                //cout<<j<<endl;
                dp[j+a[i].c]=max(dp[j+a[i].c],dp[j]+a[i].a-(a[i].b*(j+a[i].c)));
            }
        }
        int ans=0;
        for(int i=0;i<=t;++i)ans=max(ans,dp[i]);
        printf("%d\n",ans);
    }
}

Problem C: 火神的鱼

Time Limit: 5000 ms Memory Limit: 256 MB

Description

火神最爱的就是吃鱼了,所以某一天他来到了一个池塘边捕鱼。池塘可以看成一个二维的平面,而他的渔网可以看成一个与坐标轴平行的矩形。

池塘里的鱼不停地在水中游动,可以看成一些点。有的时候会有鱼游进渔网,有的时候也会有鱼游出渔网。所以火神不知道什么时候收网才可以抓住最多的鱼,现在他寻求你的帮助。

他对池塘里的每条鱼都给予了一个标号,分别从1到n标号,n表示池塘里鱼的总数。鱼的游动可以概括为两个动作:

1 l r d : 表示标号在[l,r]这个区间内的鱼向x轴正方向游动了d个单位长度。

2 l r d:表示标号在[l,r]这个区间内的鱼向y轴正方向游动了d个单位长度。

在某些时刻,火神会询问你现在有多少条鱼在渔网内(边界上的也算),请你来帮助他吧。

Input

第一行包含一个整数T,表示测试数据组数。对于每组测试数据:

第一行包含一个整数n,表示鱼的总数。

第二行包含四个整数x1,y1,x2,y2,表示渔网的左下角坐标和右上角坐标。

接下来n行,每行两个整数xi,yi,表示标号为i的鱼初始时刻的坐标。

再接下来一行包含一个整数m,表示后面的事件数目。

再接下来的m行,每行为以下三种类型的一种:

1 l r d : 表示标号在[l,r]这个区间内的鱼向x轴正方向游动了d个单位长度。

2 l r d:表示标号在[l,r]这个区间内的鱼向y轴正方向游动了d个单位长度。

3 l r : 表示询问现在标号在[l,r]这个区间内的鱼有多少在渔网内。

Output

对于每组数据的每个询问,输出一个整数表示对应的答案。

Sample Input
1
5
1 1 5 5
1 1
2 2
3 3
4 4
5 5
3
3 1 5
1 2 4 2
3 1 5
Sample Output
5
4

HINT

对于30%的数据满足:1≤n,m≤1000

对于100%的数据满足:1≤T≤10,1≤n,m≤30000,1≤l≤r≤n,1≤d≤10^9,x1≤x2,y1≤y2。保证任意时刻所有涉及的坐标值在[?10^9,10^9]范围内。

Solution

我们种两棵线段树,一棵维护每个点的x坐标,一棵维护y坐标。

令minx为矩阵左下角的x坐标,maxx为矩阵右上角的x坐标,miny、minx同理。

之后对每个坐标分三类:(这里只写了x坐标,y坐标以此类推)

1、x<minx 这种情况我们记录一下他到minx的距离,然后每次修改就把这个距离减掉相应的数,小于0时,我们把它归到第二类。

2、minx<=x<=maxx 这种情况记录他到maxx的距离,同上的修改,然后对于这部分的点检测:如果其y坐标也满足在矩阵内,我们的答案就+1。如果距离小于0,归到第三类。

3、maxx<x 我们直接把这个距离设为inf就好了,然后不管他。

对于每个区间的min值,只要修改后小于0,我们就重新统计这个区间的答案。

重新统计答案的时候用树状数组记录一下每个点是否在矩阵内就可以做到\(O(log n)\)的询问了。

由于每个点只要大于maxx我们就不管了,所以每个点实际上最多只会更新2次,总共修改的复杂度是\(O(n log n)\)的。

但是不知道是我写丑了还是怎么样,反正是擦着时限过的,荣获跑得最慢。

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x=0;bool f=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=0;
    for(;isdigit(c);c=getchar())x=(x<<3)+(x<<1)+(c^'0');
    if(f)return x;return 0-x;
}
#define INF 2147483647
inline int lowbit(int x){return x&-x;}
int n;
int bit[200001];
inline void add(int x,int v){
    while(x<=n){
        bit[x]+=v;
        x+=lowbit(x);
    }
}
inline int query(int x){
    int res=0;
    while(x){
        res+=bit[x];
        x-=lowbit(x);
    }
    return res;
}
int op[2][2];
int pos[200001][2];
bool in[200001];
struct seg{
    int type;
    int minn[200001];
    int tag[200001];
    inline void change(int o,int l){
        add(l,-in[l]);
        in[l]=(pos[l][0]>=op[0][0]&&pos[l][0]<=op[0][1]&&pos[l][1]>=op[1][0]&&pos[l][1]<=op[1][1]);
        add(l,in[l]);
        if(pos[l][type]<op[type][0])minn[o]=op[type][0]-pos[l][type];
        else if(pos[l][type]<=op[type][1])minn[o]=op[type][1]-pos[l][type];
        else minn[o]=INF;
    }
    void build(int o,int l,int r){
        tag[o]=0;
        if(l==r){
            change(o,l);
            return;
        }
        int mid=(l+r)/2;
        build(o*2,l,mid);
        build(o*2+1,mid+1,r);
        minn[o]=min(minn[o*2],minn[o*2+1]);
    }
    void pushdown(int o){
        tag[o*2]+=tag[o],tag[o*2+1]+=tag[o];
        minn[o*2]-=tag[o],minn[o*2+1]-=tag[o];
        tag[o]=0;
    }
    void update(int o,int l,int r){
        if(l==r){
            pos[l][type]+=tag[o];
            tag[o]=0;
            change(o,l);
            return;
        }
        pushdown(o);
        int mid=(l+r)/2;
        update(o*2,l,mid);
        update(o*2+1,mid+1,r);
        minn[o]=min(minn[o*2],minn[o*2+1]);
    }
    void modify(int o,int l,int r,int L,int R,int val){
        if(L<=l&&r<=R){
            tag[o]+=val;
            minn[o]-=val;
            if(minn[o]<=0)update(o,l,r);
            return;
        }
        pushdown(o);
        int mid=(l+r)/2;
        if(L<=mid)modify(o*2,l,mid,L,R,val);
        if(mid<R)modify(o*2+1,mid+1,r,L,R,val);
        minn[o]=min(minn[o*2],minn[o*2+1]);
    }
}t[2];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        memset(in,0,sizeof(in));
        memset(bit,0,sizeof(bit));
        n=read();
        op[0][0]=read(),op[1][0]=read(),op[0][1]=read(),op[1][1]=read();
        for(int i=1;i<=n;++i){
            pos[i][0]=read(),pos[i][1]=read();
        }
        t[0].type=0;
        t[1].type=1;
        t[0].build(1,1,n);
        t[1].build(1,1,n);
        int m;
        m=read();
        for(int i=1;i<=m;++i){
            int opt=read();
            if(opt==3){
                int l=read(),r=read();
                printf("%d\n",query(r)-query(l-1));
            }
            else {
                int l=read(),r=read(),val=read();
                t[opt-1].modify(1,1,n,l,r,val);
            }
        }
    }
} 

原文地址:https://www.cnblogs.com/youddjxd/p/11423233.html

时间: 2024-10-13 20:19:56

[补档]noip2019集训测试赛(十五)的相关文章

[补档]noip2019集训测试赛(十二)

Problem A: 记忆(memory) Time Limit: 1000 ms Memory Limit: 512 MB Description 你在跟朋友玩一个记忆游戏. 朋友首先给你看了n个长度相同的串,然后从中等概率随机选择了一个串. 每一轮你可以询问一个位置上的正确字符,如果能够凭借已有的信息确定出朋友所选的串,那么游戏就结束了,你的成绩就是所用的轮数. 由于你实在太笨,不会任何策略,因此你采用一种方法,每次等概率随机询问一个未询问过的位置的字符. 现在你想知道,在这种情况下,你猜出

[补档]noip2019集训测试赛(八)

Problem B: 2048 Special Judge Time Limit: 1000 ms Memory Limit: 256 MB Description 2048曾经是一款风靡全球的小游戏. 今天,我们换一种方式来玩这个小游戏. 现在,你有一个双端队列,你只能把元素从左端或从右端放入双端队列中.一旦放入就不得取出.放入后,若队列中有连续两个相同的元素,它们将自动合并变成一个新的元素--原来那两个元素的和.若新的元素与它相邻的元素相同,则继续合并-- 如:双端队列中有2, 4, 16三

[2016北京集训测试赛(五)]打地鼠-[思考题]

Description Solution 我们先只考虑一只地鼠的情况,依题意得,在某一个时刻该地鼠的可能停留位置是一个公差为2的等差数列.我们设这个等差数列的两端为[L,R].则如果区间[L+1,R-1]的格子被打实际上是不会影响L和R的(列一个等差数列实际模拟一下就发现啦).而如果格子L被打,则L+2:如果格子R被打,则R-2.打了格子后,别忘了L--,R++. 嗯根据以上性质,我们可以知道,地鼠1,3,5,7,9...的L是非递减的,地鼠2,4,6,8,10...的L也是非递减的. 然后看一

noip2019集训测试赛(二)

Problem A: 余数 Time Limit: 1000 ms Memory Limit: 512 MB Description Input Output Sample Input 3 4 Sample Output 4 HINT Solution 那个所谓\(\sqrt n\)的东西叫做整除分块. 显然对于\(n÷i\),当$i<=\sqrt n $时,每个i都有一种取法 当\(n>=i>\sqrt n\),i每加\(\sqrt n\),\(n÷i\)的值就+1 然后就可以根号时间

noip2019集训测试赛(四)

Problem A: fibonacci Time Limit: 3000 ms Memory Limit: 256 MB Description Input 第一行两个数 N,M . 第二行 N 个数 a1,a2,...,an . 接下来 M 行, 每行代表题目描述中的一种操作. Output 对于每个询问, 输出一行, 表示答案. Sample Input 5 4 1 1 2 1 1 2 1 5 1 2 4 2 2 2 4 2 1 5 Sample Output 5 7 9 HINT 对于

noip2019集训测试赛(七)

Problem A: Maze Time Limit: 1000 ms Memory Limit: 256 MB Description 考虑一个N×M的网格,每个网格要么是空的,要么是障碍物.整个网格四周都是墙壁(即第1行和第n行,第1列和第m列都是墙壁),墙壁有且仅有两处开口,分别代表起点和终点.起点总是在网格左边,终点总是在网格右边.你只能朝4个方向移动:上下左右.数据保证从起点到终点至少有一条路径. 从起点到终点可能有很多条路径,请找出有多少个网格是所有路径的必经网格. Input 第一

【2016北京集训测试赛(八)】 crash的数列

Description 题解 题目说这是一个具有神奇特性的数列!这句话是非常有用的因为我们发现,如果套着这个数列的定义再从原数列引出一个新数列,它居然还是一样的...... 于是我们就想到了能不能用多点数列套着来加速转移呢? 但是发现好像太多数列套起来是可以烦死人的...... 我们就采用嵌套两次吧,记原数列为A,第一层嵌套为B,第二层嵌套为C. 我们其实可以发现一些规律,对于Ci,它对应了B中i的个数:对于Bi,它对应了A中i的个数. 稍加处理即可,我们一边计算一边模拟数列的运算,同时可以计算

2018冬令营模拟测试赛(五)

2018冬令营模拟测试赛(五) [Problem A][UOJ#154]列队 试题描述 picks 博士通过实验成功地得到了排列 \(A\),并根据这个回到了正确的过去.他在金星凌日之前顺利地与丘比签订了契约,成为了一名马猴烧酒. picks 博士可以使用魔法召唤很多很多的猴子与他一起战斗,但是当猴子的数目 \(n\) 太大的时候,训练猴子就变成了一个繁重的任务. 历经千辛万苦,猴子们终于学会了按照顺序排成一排.为了进一步训练,picks 博士打算设定一系列的指令,每一条指令 \(i\) 的效果

2016集训测试赛(二十五)小结

这场测试赛有必要小结一下. 昨晚 1 点才睡, 今天状态很差, 先睡了 1 个小时, 然后开始打比赛. 第一题不大会做, 写了一个代码后发现是错的, 第二题看不懂题, 第三题简单地分析了一下, 发现是一个树形DP . 然后做 T3 , 大概推了很久, 写了很久, 又写了几个对拍, 搞到 11 点才搞掂. 这时候我发现 T1 有 50 分是我可做的, 然后 T2 的题意仍然不是很明确, 我想尝试着写写 T2 , 这个必须要写出来才能看出题意是不是这样, 写着写着发现 T2 的题意不是我理解的这样,