[补档]noip2019集训测试赛(十五)
Posted youddjxd
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[补档]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);
以上是关于[补档]noip2019集训测试赛(十五)的主要内容,如果未能解决你的问题,请参考以下文章