第一題:守衛(guard)
有n(2<=n<=20)頭奶牛在玩飛盤,可是飛盤飛到了高處。現在他們要想辦法疊在一起,去取飛盤。飛盤的高度爲H(1 <= H <= 1,000,000,000)。給出每頭奶牛的重量、高度、強壯度(能夠承受的重量),問他們是否夠得到,如果能夠取到,求它們額外還能承受最多多少重量。(要保證每頭奶牛所承受的重量都不超過它的強壯度)。
輸入格式:
第一行包含N和H。
接下來N行,每行三個數,分別表示它的高度、重量和強壯度。所有的數都爲正整數。
輸出格式:
如果奶牛的隊伍可以夠到飛盤,輸出還能承受的最大額外重量;否則輸出“Mark is too tall”.
成績:AC(qwq)
題解:把奶牛按“體重+承重”從大到小排序,然後dfs,每頭奶牛上面只能放“體重+承重”比它小的奶牛。
證明:(W表示放在奶牛A,B上的總重)
A在上:ans=min(strong[A]-W,strong[B]-w[A]-W);
B在上:ans=min(strong[B]-W,strong[A]-w[B]-W);
若ansA>ansB:移項=>stron[A]+w[A]>strong[B]+w[B];
分析:以前做過的原題,勉強還記得搜索,按着這個方向,雖然想(回憶)了半天,最後也憑着記憶推出來了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
using namespace std;
const long long inf=0x3f3f3f3f;
inline void getint(long long&num){
char c;long long 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;
}
struct node{
long long w,h,s,ws;
bool operator < (const node &x)const{
return ws>x.ws;
}
}cow[25];
long long n,H,ans=-inf,way[25];
bool vis[25];
void dfs(long long now,long long hight,long long cnt){
if(hight>=H){
long long Min=inf;
for(long long i=1;i<=cnt;i++)
Min=min(Min-cow[way[i]].w,cow[way[i]].s);
ans=max(ans,Min);
}
else
for(long long i=now;i<=n;i++)
if(!vis[i]){
vis[i]=1,way[cnt+1]=i;
dfs(i+1,hight+cow[i].h,cnt+1),vis[i]=0;
}
}
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].s);
cow[i].ws=cow[i].w+cow[i].s;
}
sort(cow+1,cow+n+1);
dfs(1,0,0);
if(ans>0) printf("%lld\n",ans);
else printf("Mark is too tall\n");
}
第二題:馬拉松比賽(marathon)
奶牛貝西給他的小夥伴設計了一條馬拉松比賽的線路。這條線路上一共有n(n<=100000)個點,它們分佈在一個平面座標系中。它的小夥伴毅力不夠,所以不能跑完全程,於是貝西給他們安排了一條子線路(它是一段連續的點,比如設定子線路爲【i,j】,表示奶牛需要從點i開始,經過點i+1,點i+2,……,最後到點j)。即使這樣,奶牛們仍然可能跳過中間某個點以節省路程,當然這個點不能是子線路的起點或終點。現在,有Q(Q<100000)個操作:操作分爲兩種,第一種爲”U I X Y”,表示將點I的位置設在座標(X,Y)處;第二種爲QI J,表示設定子線路爲點i到點j,需要查詢奶牛們從i跑到j的最短路程。(路程是以曼哈頓距離計算的)他們可以跳過中間某個點。
所有的座標值x,y均在[-1000,+1000]區間。
輸入:
第一行包含N和Q。接下來N行,每行兩個數(X,Y),表示每個點的座標,按照編號從小到大的順序給出。
接下來Q行,每行表示一個操作。操作如上所述。
輸出:對於每一次查詢,輸出一個整數,表示該子線路的最短路程(曼哈頓距離)。
成績:根本沒寫(TAT)
題解:一個dis數組,dis[i][0]表示i到i+1的距離,dis[i][1]表示i到i+2的距離,一顆線段樹維護l到r中最大的dis[i][0]+dis[i+1][0]-dis[i][1],一顆線段樹維護l到r不跳過點的距離。
分析:下午考的時候第一遍看沒什麼思路,就跳過了,結果晚自習回來再看突然靈光乍現2333333333333,然後又把曼哈頓距離搞錯了2333333
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
#define lson num<<1
#define rson num<<1|1
using namespace std;
const int N=100000+10;
inline 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;
}
struct node{
int x,y;
}point[N];
struct T1{
int l,r,Max;
}tree[N<<2];
struct T2{
int l,r,sum;
}sum[N<<2];
int n,T,pos,x,y,l,r,dis[N][2];
int cut,ans;
char op[3];
int abs(int x){
return x>0?x:-x;
}
int Dist(int i,int j){
return abs(point[i].x-point[j].x)+abs(point[i].y-point[j].y);
}
void updateT1(int num){
tree[num].Max=max(tree[lson].Max,tree[rson].Max);
}
void updateT2(int num){
sum[num].sum=sum[lson].sum+sum[rson].sum;
}
void build(int num,int l,int r){
sum[num].l=tree[num].l=l;
sum[num].r=tree[num].r=r;
sum[num].sum=tree[num].Max=0;
if(l==r) return ;
int mid=(l+r)>>1;
build(lson,l,mid),build(rson,mid+1,r);
}
void insertT1(int num,int pos,int val){
if(pos<tree[num].l||pos>tree[num].r) return ;
else if(pos==tree[num].l&&pos==tree[num].r)
tree[num].Max=val;
else insertT1(lson,pos,val),insertT1(rson,pos,val),updateT1(num);
}
void insertT2(int num,int pos,int val){
if(pos<sum[num].l||pos>sum[num].r) return ;
else if(pos==sum[num].l&&pos==sum[num].r)
sum[num].sum=val;
else{
insertT2(lson,pos,val);
insertT2(rson,pos,val);
updateT2(num);
}
}
void calT1(int num,int l,int r){
if(tree[num].l>r||tree[num].r<l) return ;
else if(tree[num].l>=l&&tree[num].r<=r)
cut=max(cut,tree[num].Max);
else calT1(lson,l,r),calT1(rson,l,r);
}
void calT2(int num,int l,int r){
if(sum[num].l>r||sum[num].r<l) return ;
else if(sum[num].l>=l&&sum[num].r<=r)
ans+=sum[num].sum;
else calT2(lson,l,r),calT2(rson,l,r);
}
int main(){
getint(n),getint(T);
for(int i=1;i<=n;i++)
getint(point[i].x),getint(point[i].y);
for(int i=1;i<n;i++){
dis[i][0]=Dist(i,i+1);
if(i<n-1) dis[i][1]=Dist(i,i+2);
}
build(1,1,n);
for(int i=1;i<=n;i++){
if(i<n-1)
insertT1(1,i,dis[i][0]+dis[i+1][0]-dis[i][1]);
insertT2(1,i,dis[i][0]);
}
while(T--){
scanf("%s",op);
if(op[0]=='U'){
getint(pos),getint(x),getint(y);
point[pos].x=x,point[pos].y=y;
if(pos>1) dis[pos-1][0]=Dist(pos-1,pos);
if(pos>2) dis[pos-2][1]=Dist(pos-2,pos);
if(pos<n) dis[pos][0]=Dist(pos,pos+1);
if(pos<n-1) dis[pos][1]=Dist(pos,pos+2);
if(pos>1||pos<n) insertT1(1,pos-1,dis[pos-1][0]+dis[pos][0]-dis[pos-1][1]);
if(pos>2) insertT1(1,pos-2,dis[pos-2][0]+dis[pos-1][0]-dis[pos-2][1]);
if(pos<n-1) insertT1(1,pos,dis[pos][0]+dis[pos+1][0]-dis[pos][1]);
insertT2(1,pos,dis[pos][0]);
if(pos>1) insertT2(1,pos-1,dis[pos-1][0]);
if(pos>2) insertT2(1,pos-2,dis[pos-2][0]);
}
else{
getint(l),getint(r);
cut=0,ans=0,calT1(1,l,r-2),calT2(1,l,r-1);
printf("%d\n",ans-cut);
}
}
}
第三題:奶牛慢跑(cowjog)
有n(n<=100000)頭奶牛在一個無窮長的小道上慢跑。每頭奶牛的起點不同,速度也不同。小道可以被分成多條跑到。奶牛只能在屬於自己的跑道上慢跑,不允許更換跑道,也不允許改變速度。如果要慢跑t(t<=1000000000)分鐘,要保證在任何時候不會有同一跑道上的奶牛相遇,請問最少需要多少條跑道。奶牛開始在哪條跑道是可以隨意設置的。
輸入格式:第一行兩個整數n,t。
接下來的n行,每行包含兩個整數,表示奶牛的位置和速度。位置是非負整數,速度是正整數。所有的奶牛的起點都不相同,按起點遞增的順序給出。
輸出格式:最少的跑道數。
成績:2333333333(拒絕寫出)(其實並不知道,應該是全WA =_=)
題解:按距離排序,然後最長下降子序列(同攔截導彈)
分析:其實考試時想出了正解,然後只剩下十幾分鍾了,一時腦殘,用樹狀數組把最長下降子序列打成了一個倒着的逆序對還找了半天錯(233333333,被自己傻b到),然後後來過題時有放棄了這個思路,想了個詭異的:用一個dp數組記錄,每一組的最遠距離,新加入一個人是二分查找放在哪一組裏(儘量前放)(就是查找一組的dp值小於新加入的人跑的距離)
正解代碼(線段樹優化最長下降子序列)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
#define lson num<<1
#define rson num<<1|1
using namespace std;
const int N=100000+10;
inline void getLL(long long&num){
char c;int flag=1;num=0LL;
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 n,ans=1,tmp;
long long t;
struct node{
long long pos,v,w;
bool operator < (const node &x)const{
if(pos+t*v==x.pos+t*x.v) return pos>x.pos;
return pos+t*v<x.pos+t*x.v;
}
}cow[100010];
struct T{
int l,r,Max;
T(){Max=1;}
}tree[N<<2];
void update(int num){
tree[num].Max=max(tree[lson].Max,tree[rson].Max);
}
void build(int num,int l,int r){
tree[num].l=l,tree[num].r=r;
if(l==r) return ;
int mid=(l+r)>>1;
build(lson,l,mid),build(rson,mid+1,r);
}
void insert(int num,int pos,int val){
if(pos<tree[num].l||pos>tree[num].r) return ;
else if(tree[num].l==pos&&tree[num].r==pos)
tree[num].Max=val;
else insert(lson,pos,val),insert(rson,pos,val),update(num);
}
void getmax(int num,int l,int r){
if(r<tree[num].l||l>tree[num].r) return ;
else if(tree[num].l>=l&&tree[num].r<=r)
tmp=max(tmp,tree[num].Max);
else getmax(lson,l,r),getmax(rson,l,r);
}
int main(){
scanf("%d",&n),getLL(t);
for(int i=1;i<=n;i++)
getLL(cow[i].pos),getLL(cow[i].v),cow[i].w=i;
sort(cow+1,cow+n+1);
build(1,1,n);
for(int i=2;i<=n;i++){
cow[i].w=n-cow[i].w+1;
tmp=1,getmax(1,1,cow[i].w-1);
insert(1,cow[i].w,tmp+1);
ans=max(ans,tmp);
}
printf("%d\n",ans);
}
解法二:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
using namespace std;
const long long inf=1000000000000000000LL;
const int N=100000+10;
inline void getLL(long long&num){
char c;int flag=1;num=0LL;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
num*=flag;
}
long long n,ans=1LL,t,tree[N],dp[N];
struct node{
long long pos,v,w;
}cow[N];
long long find(long long l,long long r,long long x){
if(l>r) return l;
int mid=(l+r)>>1;
if(x==dp[mid]) return mid+1;
else if(x<dp[mid]) return find(mid+1,r,x);
else return find(l,mid-1,x);
}
int main(){
scanf("%lld",&n),getLL(t);
for(int i=1;i<=n;i++){
getLL(cow[i].pos),getLL(cow[i].v);
cow[i].w=cow[i].pos+cow[i].v*t,dp[i]=inf;
}
dp[1]=cow[1].w;
for(int i=2;i<=n;i++){
long long pos=find(1,ans,cow[i].w);
dp[pos]=cow[i].w,ans=max(ans,pos);
}
printf("%lld\n",ans);
}
總結:因爲時間比較短,(非常非常非常非常非常非常非常非常非常非常來不及)第一題耽誤的時間比較多(明明A過,卻想了很久23333333),如果沒做過就並不清楚能不能A了(感覺希望不大),第二題的話,其實並不清楚在完整時間內能否想出來,但後來實現代碼時還算沒遇到什麼困難。(然而好像是oj上最慢的一個@_@)第三題,(2333)是真不知道O(nlogn)的最長下降子序列的算法(223333),但寫成“順序對”表示完全被自己傻B到(233)(樣例水得無法直視2333333),至少也應該寫一個O(n^2)的騙分(23333)。