总述:
这场考试只考了一半就走了,当时我还没做完第二题,后面是晚自习回来继续做的。
第一题看着万分地熟悉,就感觉肯定是个Dp,因为以前好像是跟着XXX大学长学姐们听过解题思路的,但是我想不起来了,然后花了大概20分钟想dp,我推出来一个表达式:若w[i]+st[i]<w[j]+st[j],那么i放在下面,j放在上面,然后我排序了,之后就Dfs觉得没什么问题,最后很惊喜地A了特开心。
第二题是区间内的查询问题,第一反应就是线段树,然后就需要去处理删除一个点后的情况。写题的时候其实有些混乱,思路还不是特别地清晰,起码没有现在清晰,然后我就把思路写在了草稿纸上,嗯我认为这是个好办法,它对我特别有用,这样错误的思路会马上被发现,因为白纸黑字会推不通,正确的思路也可以让自己更清楚下一步要干什么,怎么样还可不可以优化一点,最不济也不会绕来绕去把自己绕进去,起码我常常干这种事(昨天物理考试因为算出来答案带根号我就没敢往上写,结果事实证明那就是正确答案,我真是太天真太善良了)最后写这题其实还是蛮多细节需要注意的,自己逻辑真的不太强,典型的想到什么是什么。
第三题我确实没有想出来,还是后来问了同学才恍然大悟的。个人觉得这对我来说是一个盲点,我想题的过程中基本上不会考虑二分这种做法,除非是那种特别直白傻缺特别明摆着的二分的题,虽然二分也写了一些题,但是经常会由于某种原因而wa二分(特别是要分后半截区间的时候,那种大于等于,严格小于什么的总是出问题),所以其实并不是特别敢写二分,这算是一个缺漏的知识点,正排时间打算把它补上。
题目:
问题 A(3039): 奶牛飞盘
时间限制: 1 Sec 内存限制: 128 MB题目描述
有n(2<=n<=20)头奶牛在玩飞盘,可是飞盘飞到了高处。现在他们要想办法叠在一起,去取飞盘。飞盘的高度为H(1 <= H <= 1,000,000,000)。给出每头奶牛的重量、高度、强壮度(能够承受的重量),问他们是否够得到,如果能够取到,求它们额外还能承受最多多少重量。(要保证每头奶牛所承受的重量都不超过它的强壮度)。
输入
输入格式:
第一行包含N和H。
接下来N行,每行三个数,分别表示它的高度、重量和强壮度。所有的数都为正整数。
输出
输出格式:
如果奶牛的队伍可以够到飞盘,输出还能承受的最大额外重量;否则输出“Mark is too tall”.
样例输入
4 109 4 13 3 55 5 104 4 5
样例输出
2
很容易会想到要把重的放在下面或者把称重量大的放在下面,但是单一的排序很容易就会找出反例,不如来作这样一个假设,将st和w相加,再排序看一看。
对于第i和第i+1头牛,有两种放法:
i在上时:
i:st[i]-w
i+1:st[i+1]-w[i]-w
i在下时:
i:st[i]-w[i+1]-w
i+1:st[i+1]-w
对i和i+1进行假设,并尝试调换位置后可得表达式w[i]+st[i]>w[i+1]+st[i+1]时,i在下
然后搜索
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
const int Max=20;
int N,H,Ans;
bool flag;
struct node{
int h,w,st,wt;
bool operator <(const node&X)const{
return wt>X.wt;
}
}Cow[Max+5];
bool vis[Max+5];
int min(int a,int b){return a<b?a:b;}
void getint(int &num){
char c;int flag=1;num=0;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
num*=flag;
}
void Dfs(int i,int h,int lft){
if(h>=H&&lft>=0){
flag=1;
Ans=max(Ans,lft);
return ;
}
if(lft<=0||i==N)return ;
for(int j=i+1; j<=N; ++j)
Dfs(j,h+Cow[j].h,min(lft-Cow[j].w,Cow[j].st));
}
int main(){
//freopen("guard.in","r",stdin);
//freopen("guard.out","w",stdout);
getint(N),getint(H);
for(int i=1; i<=N; ++i){
getint(Cow[i].h),getint(Cow[i].w),getint(Cow[i].st);
Cow[i].wt=Cow[i].w+Cow[i].st;
}
sort(Cow+1,Cow+1+N);
for(int i=1; i<=N; ++i)
Dfs(i,Cow[i].h,Cow[i].st);
if(flag)
printf("%d\n",Ans);
else puts("Mark is too tall");
return 0;
}
问题 B(3040): 马拉松
时间限制: 1 Sec 内存限制: 128 MB题目描述
奶牛贝西给他的小伙伴设计了一条马拉松比赛的线路。这条线路上一共有n(n<=100000)个点,它们分布在一个平面坐标系中。它的小伙伴毅力不够,所以不能跑完全程,于是贝西给他们安排了一条子线路(它是一段连续的点,比如设定子线路为【i,j】,表示奶牛需要从点i开始,经过点i+1,点i+2,……,最后到点j)。即使这样,奶牛们仍然可能跳过中间某个点以节省路程,当然这个点不能是子线路的起点或终点。现在,有Q(Q<100000)个操作:操作分为两种,第一种为”U I X Y”,表示将点I的位置设在坐标(X,Y)处;第二种为Q I J,表示设定子线路为点i到点j,需要查询奶牛们从i跑到j的最短路程。(路程是以曼哈顿距离计算的)他们可以跳过中间某个点。
所有的坐标值x,y均在[-1000,+1000]区间。
输入
输入:
第一行包含N和Q。接下来N行,每行两个数(X,Y),表示每个点的坐标,按照编号从小到大的顺序给出。
接下来Q行,每行表示一个操作。操作如上所述。
输出
输出:对于每一次查询,输出一个整数,表示该子线路的最短路程(曼哈顿距离)
样例输入
5 5
-4 4
-5 -3
-1 5
-3 4
0 5
Q 1 5
U 4 0 1
U 4 -1 1
Q 2 4
Q 1 4
样例输出
11
8
8
线段树去保存每个区间里删除后变小得最多的那个点引起变化的delta值,合并的时候只需考虑删除mid会不会更优的情况,更改的时候注意它的左右父亲(因为线段的单位是区间)都要更改,然后正常地查询就可以了,小心爆int。
#include<iostream>
#include<cstdio>
#include<climits>
#include<cstring>
using namespace std;
typedef long long LL;
const int Max=100000;
struct node{
int x,y;
}P[Max+5];
struct trr{
int l,r,d,delta;
}Tr[Max*4];
int N,Q;
//int code[Max+5];
int abs(int a){return a<0?-a:a;}
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a<b?b:a;}
void getint(int &num){
char c;int flag=1;num=0;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
num*=flag;
}
int get_dis(int a,int b){return abs(P[a].x-P[b].x)+abs(P[a].y-P[b].y);}
void build(int i,int l,int r){
Tr[i].l=l,Tr[i].r=r;
if(l+1==r){
Tr[i].d=get_dis(l,r);
Tr[i].delta=INT_MAX;
//code[l]=i;
return ;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid,r);
Tr[i].d=Tr[i<<1].d+Tr[i<<1|1].d;
Tr[i].delta=min(Tr[i<<1].delta,min(Tr[i<<1|1].delta,get_dis(mid-1,mid+1)-get_dis(mid-1,mid)-get_dis(mid,mid+1)));
}
void change(int i,int s){
if(s<Tr[i].l||s>Tr[i].r) return ;
if(Tr[i].l+1==Tr[i].r){
Tr[i].d=get_dis(Tr[i].l,Tr[i].r);
return ;
}
int mid=(Tr[i].l+Tr[i].r)>>1;
change(i<<1,s);
change(i<<1|1,s);
Tr[i].d=Tr[i<<1].d+Tr[i<<1|1].d;
Tr[i].delta=min(Tr[i<<1].delta,min(Tr[i<<1|1].delta,get_dis(mid-1,mid+1)-get_dis(mid-1,mid)-get_dis(mid,mid+1)));
}
LL get_ans(int i,int l,int r,LL &delta){
if(Tr[i].l>=r||Tr[i].r<=l) return 0;
if(l<=Tr[i].l&&Tr[i].r<=r){
delta=min(delta,Tr[i].delta);
return Tr[i].d;
}
int mid=(Tr[i].l+Tr[i].r)/2;
if(l<mid&&mid<r) delta=min(delta,get_dis(mid-1,mid+1)-get_dis(mid-1,mid)-get_dis(mid,mid+1));
return get_ans(i<<1,l,r,delta)+get_ans(i<<1|1,l,r,delta);
}
int main(){
getint(N),getint(Q);
for(int i=1; i<=N; ++i)
getint(P[i].x),getint(P[i].y);
build(1,1,N);
char ord[3];
int s,t,x,y;
for(int i=1; i<=Q; ++i){
scanf("%s",ord);
if(ord[0]=='Q'){
getint(s),getint(t);
LL delta=0;
LL Ans=get_ans(1,s,t,delta);
if(delta<0) Ans+=delta;
printf("%lld\n",Ans);
}
else {
getint(s),getint(x),getint(y);
P[s].x=x,P[s].y=y;
change(1,s);
}
}
return 0;
}
问题 C(3041): 奶牛慢跑
时间限制: 1 Sec 内存限制: 128 MB题目描述
有n(n<=100000)头奶牛在一个无穷长的小道上慢跑。每头奶牛的起点不同,速度也不同。小道可以被分成多条跑到。奶牛只能在属于自己的跑道上慢跑,不允许更换跑道,也不允许改变速度。如果要慢跑t(t<=1000000000)分钟,要保证在任何时候不会有同一跑道上的奶牛相遇,请问最少需要多少条跑道。奶牛开始在哪条跑道是可以随意设置的。
输入
输入格式:第一行两个整数n,t。
接下来的n行,每行包含两个整数,表示奶牛的位置和速度。位置是非负整数,速度是正整数。所有的奶牛的起点都不相同,按起点递增的顺序给出。
输出
输出格式:
最少的跑道数。
样例输入
5 30 11 22 33 26 1
样例输出
3
我们可以算出每头奶牛的起点和终点,模拟有N条跑道,看哪些奶牛可以尽可能地放进之前的跑道里,二分去找一个最小的大于当前的终点值的跑道,将其放进去。
#include<iostream>
#include<cstdio>
#include<climits>
using namespace std;
typedef long long LL;
const int Max=100000;
struct node{
int p,v;
LL end;
}Cow[Max+5];
int N,T,Ans=1;
LL E[Max+5];//第i条跑道的最靠后一人的结束位置
void getint(int &num){
char c;int flag=1;num=0;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
num*=flag;
}
int upbound(LL val){//找小于等于当前结束位置的最大位置的后一个
int l=1,r=Ans,mid;
while(l<=r){
mid=(l+r)>>1;
if(val==E[mid]) return mid+1;
else if(val<E[mid]) l=mid+1;
else r=mid-1;
}
return l;
}
int main(){
getint(N),getint(T);
for(int i=1; i<=N; ++i){
getint(Cow[i].p),getint(Cow[i].v);
Cow[i].end=Cow[i].p+1LL*Cow[i].v*T;
E[i]=LONG_LONG_MAX;
}
E[1]=Cow[1].end;
for(int i=2; i<=N; ++i){
int pos=upbound(Cow[i].end);
E[pos]=Cow[i].end,Ans=max(Ans,pos);
}
printf("%d\n",Ans);
return 0;
}