T1
题目描述:
洪蛤吨来到了一条有NN家商店的步行街,商店从左往右依次从11标号到NN。每家商店都买卖同一件产品,且都有同样的规矩:每个来到店里的人,只能买一件产品,卖一件产品,或者什么都不做。有趣的是,在同一家店里买卖产品,支出或者收到的钱是一样的。具体的,如果洪蛤吨在第ii家店里买/卖一件产品,那么洪蛤吨将会支出/收到AiAi单位的货币。
洪蛤吨将从11号商店依次走到NN号商店,一开始洪蛤吨有无限的初始资金,但并没有任何一件产品。
洪蛤吨想知道,当他完成这次步行的时候,他的收益最大是多少。在此前提下,洪蛤吨还希望进入店内买/卖产品的次数尽量少。
输入格式
第一行一个整数TT,表示数据组数
对于每组数据,第一行一个整数NN,接下来一行NN个整数,第ii个整数表示AiAi。
输出格式
对于每组数据,输出一行两个整数表示最大收益和在满足最大收益的前提下的最小交易次数,请注意,答案可能会很大。
样例输入
2
3
1 2 3
3
3 2 1
样例输出
2 2
0 0
数据规模
对于 15%15% 的数据,N≤10N≤10。
对于 25%25% 的数据,N≤16N≤16。
对于 45%45% 的数据,N≤200N≤200。
对于 70%70% 的数据,N≤2000N≤2000。
对于 100%100% 的数据, 1≤T≤51≤T≤5,1≤N≤500001≤N≤50000,1≤Ai≤1091≤Ai≤109。
题目信息
题目类型:传统型
输入文件:标准输入
输出文件:标准输出
时间限制:1 s
空间限制:512 MB
题解:这道题,一开始我们猜了一个错误的结论,然后用线段树维护了半天,最后还暴零了,水啊水啊
一开始猜的结论是这样的,最大的肯定和最小的匹配才对,但并非如此,我们来看这个,
1 4 2 9 若用最大与最小匹配,那么答案为,8 但是,其实答案为10,。。。
怎么解决这种问题呢,我们可以用一个优先级队列来维护,并且维护这个点是否用过,若用过,但遇到更优
情况,我们就用ans+=(a[i]-Q.frist)但是交换次数不加,就相当于我们可以反悔,若遇到最优解,所以在把,
被反悔的值加回队列里面去就行了,
总结:以后写题,还是要先写暴力,既可以保证最后不出锅,也可以验证自己结论的正确性,这次考试给自己一个深刻的教训啊
代码:
#include<cstdio> #include<algorithm> #include<queue> using namespace std; struct my{ int v,d; bool operator<(const my &rhs)const{ if(rhs.v==v) return d>rhs.d; return v>rhs.v; } }; const int maxn=100000+10; priority_queue<my>Q; int a[maxn]; int main(){ int t; scanf("%d",&t); while(t--){ while(!Q.empty()) Q.pop(); int n; scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); long long ans=0; int num=0; my u; u.v=a[1]; u.d=1; Q.push(u); for (int i=2;i<=n;i++){ u=Q.top(); if(a[i]>u.v){ Q.pop(); ans+=(a[i]-u.v); num+=u.d; if(u.d==0) Q.push((my){u.v,1}); Q.push((my){a[i],0}); } else { Q.push((my){a[i],1}); } } printf("%lld %d ",ans,num*2); } return 0; }
T2
背景描述
洪蛤吨有一个宽度为LL的管道,我们可以视其为一条y=Ly=L的直线与YY轴所夹的部分。
(所以这根管道的长度可以视为正无穷)
这个管道中有个NN个障碍点,第ii个障碍点的坐标为(Xi,Yi)(Xi,Yi)。
已知有一个球体,能在不碰到障碍点的前提下,从管道的最左端走到最右端。(即从管道内横坐标负无穷的地方走到横坐标正无穷的地方)。
求这个球体的直径最大是多少。为了避免精度误差,请输出答案保留三位小数的结果。
输入格式
第一行两个整数N,LN,L。
接下来NN行,每行两个整数Xi,YiXi,Yi。
输出格式
一行一个三位小数表示答案。
样例输入
1 5
2 2
样例输出
3.000
数据规模
对于 30%30% 的数据,N≤3N≤3。
对于 60%60% 的数据,N≤80N≤80。
对于 100%100% 的数据, 1≤N≤5001≤N≤500,1≤Yi<L≤100001≤Yi<L≤10000,−10000≤Xi≤10000−10000≤Xi≤10000。
题目信息
题目类型:传统型
输入文件:标准输入
输出文件:标准输出
时间限制:1 s
空间限制:512 MB
题解:这道题目是最可惜的,一开始就已经想到了是二分答案,但是不知道怎么检查,然后就去写第一题,(自以为会第一题),但是。。。。。
如果继续想,应该会想到用检查方法,。。。哎,以后还是要注意考试策略
怎么检查呢,我们这样想,我们把上表面当做一个大障碍,下表面当做一个大障碍,然后只要上表面和任何一个障碍之间距离小于球的直径
也就是说球不能从这里过,我们就将其连一条边,下表面也一样,在把每两个点之间,小于球直径的连边,最后检查从上表边到下表面是否
有一条边,若有则说明有一条线隔开了两边怎么也过不去,所以直径过大,若没有说明一定可以找到一条路走到另一边,
这个判断操作可以用并查集完成,于是便完美解决这个问题
代码:
#include<cstdio> #include<algorithm> #include<cmath> using namespace std; struct my{ double x,y; }; const double eps=1e-5; const int maxn=500+10; double L; int n; int fa[maxn]; int s,t; my a[maxn]; int getfa(int x){ if(fa[x]==x) return x; return fa[x]=getfa(fa[x]); } bool meger(int x,int y){ int xx=getfa(x); int yy=getfa(y); if(xx!=yy) fa[xx]=yy; } bool check(double x){ for (int i=0;i<=t;i++) fa[i]=i; for (int i=1;i<=n;i++) if(a[i].y<x) meger(s,i); for (int i=1;i<=n;i++) if(L-a[i].y<x) meger(t,i); for (int i=1;i<=n;i++){ for (int j=i+1;j<=n;j++){ if(sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y))<x) { meger(i,j); } } } if(getfa(s)==getfa(t)) return true; return false; } int main(){ scanf("%d %lf",&n,&L); for(int i=1;i<=n;i++){ scanf("%lf %lf",&a[i].x,&a[i].y); } s=n+1; t=n+2; double l=1,r=L+10,ans=0,mid; while(l+eps<r){ mid=(l+r)/2.0; if(check(mid)) r=mid; else { ans=mid; l=mid; } } printf("%.3lf",ans); return 0; }