第一題:原子核研究
題目描述:
最近物理學家正在研究一些新的原子,稱爲X族。他們發現該族中的元素非常相近,但是其質量都不相同。質量越接近的越容易相互轉化。現在,物理學家已經發明瞭一種設備,可以對X族的元素來進行以下三種操作:
1.generate M 產生質量爲M的一個元素,如果已經存在,則該操作不產生作用。
2.romove M 銷掉質量爲M的元素,如果不存在質量爲M的元素,則該操作不產生作用。
3.query 查詢X族中質量最接近的兩種元素的質量差的絕對值。如果此時X族中的元素個數小於2,則輸出-1.
現在,請你寫一個程序來模擬該設備的操作。
輸入:輸入包含多組測試數據,第一行一個整數t,表示測試數據的組數。對於每一組數據,第一行是一個整數n,表示有n條指令。接下來n行,每行表示一條指令,如上所述。M是一個正整數,不超過100000.
題解:其實就是普通的AVL數,同時維護每一子樹上的Max,Min,ans(data-左子樹最大或右子樹最小-data),正常查詢即可。
成績:10
分析:考試的時候光想到最大最小,沒有維護ans查詢時也沒有搜下去。覺得第一題比較簡單,然後也沒有特意地編數據,TAT233333.
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
using namespace std;
const int N=100000+10;
const int inf=0x3f3f3f3f;
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 data,lc,rc,h,sum,Max,Min,ans;
node(){ans=inf;}
}tree[N];
int T,t,n,r,cnt;
char op[10];
int abs(int x){
return x>0?x:-(x);
}
void update_high(int root){
tree[root].h=max(tree[tree[root].lc].h,tree[tree[root].rc].h)+1;
tree[root].sum=tree[tree[root].lc].sum+tree[tree[root].rc].sum+1;
int tmp1,tmp2,tmp3,tmp4;
tmp1=tmp2=tmp3=tmp4=inf;
if(tree[root].rc){
tree[root].Max=tree[tree[root].rc].Max;
tmp1=tree[tree[root].rc].ans,tmp2=tree[tree[root].rc].Min-tree[root].data;
}
else tree[root].Max=tree[root].data;
if(tree[root].lc){
tree[root].Min=tree[tree[root].lc].Min;
tmp3=tree[tree[root].lc].ans,tmp4=tree[root].data-tree[tree[root].lc].Max;
}
else tree[root].Min=tree[root].data;
tree[root].ans=min(min(tmp1,tmp2),min(tmp3,tmp4));
}
int zig(int root){
int t=tree[root].lc;
tree[root].lc=tree[t].rc,tree[t].rc=root;
update_high(root),update_high(t);
return t;
}
int zag(int root){
int t=tree[root].rc;
tree[root].rc=tree[t].lc,tree[t].lc=root;
update_high(root),update_high(t);
return t;
}
int zigzag(int root){
tree[root].rc=zig(tree[root].rc) ;
return zag(root) ;
}
int zagzig(int root){
tree[root].lc=zag(tree[root].lc) ;
return zig(root);
}
void maintain(int &root){
if(tree[tree[root].lc].h==tree[tree[root].rc].h+2){
int t=tree[root].lc;
if(tree[tree[t].lc].h==tree[tree[root].rc].h+1) root=zig(root);
else if(tree[tree[t].rc].h==tree[tree[root].rc].h+1) root=zagzig(root);
}
else if(tree[tree[root].rc].h==tree[tree[root].lc].h+2){
int t=tree[root].rc;
if(tree[tree[t].rc].h==tree[tree[root].lc].h+1) root=zag(root);
else if(tree[tree[t].lc].h==tree[tree[root].lc].h+1) root=zigzag(root);
}
update_high(root);
}
int insert(int root,int x){
if(!root){
tree[++cnt].data=x;
tree[cnt].sum=tree[cnt].h=1;
tree[cnt].Max=tree[cnt].Min=x;
tree[cnt].lc=tree[cnt].rc=0;
tree[cnt].ans=inf;
return cnt;
}
else if(x<tree[root].data)
tree[root].lc=insert(tree[root].lc,x);
else if(x>tree[root].data)
tree[root].rc=insert(tree[root].rc,x);
maintain(root);
return root;
}
int del(int &root,int x){
int res;
if(tree[root].data==x||(x<tree[root].data&&!tree[root].lc)||(x>tree[root].data&&!tree[root].rc)){
res=tree[root].data;
if(!tree[root].lc||!tree[root].rc){
root=tree[root].lc+tree[root].rc;return res;
}
else tree[root].data=del(tree[root].lc,x);
}
else{
if(x<tree[root].data) res=del(tree[root].lc,x);
else res=del(tree[root].rc,x);
}
maintain(root);
return res;
}
bool find(int val){
int x=r;
while(x){
if(tree[x].data==val) return 1;
else if(tree[x].data>val) x=tree[x].lc;
else x=tree[x].rc;
}
return 0;
}
int main(){
//freopen("atomic1.in","r",stdin);
//freopen("1.out","w",stdout);
getint(T);
while(T--){
getint(t),cnt=r=0;
while(t--){
scanf("%s",op);
if(op[0]=='q') printf("%d\n",tree[r].sum>1?tree[r].ans:-1);
else{
getint(n);
if(op[0]=='g') r=insert(r,n);
else if(find(n)) del(r,n);
}
}
putchar(10);
/*for(int i=0;i<=cnt;i++){
tree[i].lc=tree[i].rc=0;
tree[i].data=tree[i].h=tree[i].sum=0;
tree[i].Max=-inf,tree[i].Min=inf,tree[i].ans=inf;
}*/
}
return 0;
}
第二題:題目描述
給出球星們的能力值、年份、名字,有很多個查詢,每個查詢給出一個年份的範圍,求出這個範圍裏能力值從高到低排列的前11名球員,如果能力值相同則按年份從低到高排,如果年份仍然相同,則按名字的字典序排。如果不足11個球員,就用XXX代替輸出湊夠11行。
輸入:第一行包含1個整數N(1<=N<=50000),表示球星的總數,接下來N行,每行描述了1個球星(year,name,ability)。0<=year<=1000000000,name不超過15個字母,0<=ability<=1000000000.
假設沒有兩個人的名字相同。接下來有一個整數R(R<=100000),表示有R個查詢。接下來R行,每行描述了一個產尋,每個查詢包含2個整數(x,y),表示從第x年到第y年。(0<=x<=y<=1000000000)
輸出:對於每組數據,按上面描述的順序輸出最靠前的11個球員的名字,每個球員佔一行。如果不足11行,則用XXX代替,湊夠11行。每組數據後都有一個空行。
成績:0
題解:把year離散化,線段樹,結構體裏開一個優先隊列,保證裏面不超過11個元素。
分析:其實考試時想到了正確思路,但寫代碼時犯了兩個錯誤,首先:數組開小了,球星加上詢問一起需要離散化的元素有2R+N個,但習慣性打成了N233,然後“每個查詢給出一個年份的範圍,求出這個範圍裏能力值從高到低排列的前11名球員,如果能力值相同則按年份從低到高排,如果年份仍然相同,則按名字的字典序排”這一句話,讀題時還標註了,最後寫代碼是能力值相同就直接名字字典序了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#define lson num<<1
#define rson num<<1|1
using namespace std;
const int N=50000+10;
const int inf=0x3f3f3f3f;
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 p{
int year,data;
char name[20];
p(){memset(name,0,sizeof(name));}
bool operator < (const p &x)const{
if(data!=x.data)return data>x.data;
else if(year!=x.year)return year<x.year;
else return strcmp(name,x.name)<0;
}
}star[N],tmp,B[300000];
struct node{
int l,r;
priority_queue<p> ans;
}tree[300000<<2];
int n,T,tot,cnt,Y[300000],ask[2];
map<int,int> q;
priority_queue <p> A;
queue <p> b;
void build(int num,int l,int r){
tree[num].l=l,tree[num].r=r;
while(!tree[num].ans.empty())
tree[num].ans.pop();
if(l==r) return ;
int mid=(l+r)>>1;
build(lson,l,mid),build(rson,mid+1,r);
}
void insert(int num,p x){
if(tree[num].r<x.year||tree[num].l>x.year) return ;
tree[num].ans.push(x);
while(tree[num].ans.size()>11) tree[num].ans.pop();
int mid=(tree[num].l+tree[num].r)>>1;
if(x.year<=mid) insert(lson,x);
else insert(rson,x);
}
void search(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){
while(!tree[num].ans.empty())
b.push(tree[num].ans.top()),tree[num].ans.pop();
while(!b.empty()){
A.push(b.front());
while(A.size()>11) A.pop();
tree[num].ans.push(b.front()),b.pop();
}
}
else search(lson,l,r),search(rson,l,r);
}
bool cmp(p a,p b){
return a.year<b.year;
}
int find(int w){
int l=0,r=n,mid;
int ans=0;
while(l<=r){
mid=(l+r)>>1;
if(Y[mid]<w) ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
int main(){
getint(n);
for(int i=1;i<=n;i++)
getint(star[i].year),scanf("%s",star[i].name),getint(star[i].data);
sort(star+1,star+n+1,cmp);
cnt=1;
for(int i=1;i<=n;++i){
Y[i]=star[i].year;
if(star[i].year!=star[i+1].year)star[i].year=cnt++;
else star[i].year=cnt;
}
cnt--;
build(1,1,cnt);
for(int i=1;i<=n;i++)
insert(1,star[i]);
getint(T);
while(T--){
getint(ask[0]),getint(ask[1]);
while(!A.empty()) A.pop();
ask[0]=star[find(ask[0])+1].year;
ask[1]=star[find(ask[1]+1)].year;
search(1,ask[0],ask[1]);
int now=0;
while(!A.empty())
B[++now]=A.top(),A.pop();
sort(B+1,B+now+1);
for(int i=1;i<=now;i++)
printf("%s\n",B[i].name);
for(now=now+1;now<=11;now++)printf("XXX\n");
putchar(10);
}
}
第三題:題目描述:公園中有許多木樁,每個木樁都有一個高度,活潑的小 z 同學喜歡從一個木樁跳到另一個木樁上,zn 說太危險了,於是 z 同學讓 zn 算出每一次跳躍的危險係數。小z 每一次跳 躍的範圍是一個 k * k 的矩形,危險係數爲這個矩形內最高的木樁高度減去最小的。身爲 oier, 你能比學數奧的 zn 算的快嗎
輸入:第一行三個整數 n(木樁爲 n ∗ n 的矩陣)、k、b(小 zz 準備跳躍的次數) 接下來爲 n ∗ n 的矩陣,描述木樁的高度接下來 b 行;每行兩個整數 x, y(表示 z 同學跳躍的範圍的左上角爲第 x 行第 y列), 保證跳躍範圍不超過矩陣的範圍
【數據範圍】對於30%的數據 0 < k ≤ n ≤ 250,0 < b ≤ 100;
對於100%的數據 0 < k ≤ n ≤ 250,0 < b ≤ 1000000
成績:0
題解:二維線段樹裸題
分析:兩個巨大錯誤:1.數組開小了;2.build函數打錯了。但即使打對了也只有30分,分析數據只有250*250,可詢問最多有1000000個二250*250=62500,中間一定有重複的(x,y)詢問,記錄ans[x][y],有重複的直接輸出即可。(這個真的沒想到,還是考完幾天聽hyh說才發現2333)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
#define son1 (num<<2)-2
#define son2 (num<<2)-1
#define son3 num<<2
#define son4 num<<2|1
using namespace std;
const int N=300;
const int inf=0x3f3f3f3f;
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 n,k,T,x,y,Max,Min,arr[N][N],ans[N][N];
bool vis[N][N];
struct node{
int x1,y1,x2,y2,Max,Min;
node(){Max=-inf,Min=inf;}
node(int a,int b,int c,int d,int e,int f){
x1=a,y1=b,x2=c,y2=d,Max=e,Min=f;
}
}tree[N*N*16];
void update(int num){
tree[num].Max=max(max(tree[son1].Max,tree[son2].Max),max(tree[son3].Max,tree[son4].Max));
tree[num].Min=min(min(tree[son1].Min,tree[son2].Min),min(tree[son3].Min,tree[son4].Min));
}
void build(int num,int x1,int y1,int x2,int y2){
tree[num]=node(x1,y1,x2,y2,-inf,inf);
if(x1==x2&&y1==y2){
tree[num].Max=tree[num].Min=arr[x1][y1];return ;
}
int midx=(x1+x2)>>1,midy=(y1+y2)>>1;
if(x1!=x2 &&y1!=y2){
build(son1,x1,y1,midx,midy);
build(son2,midx+1,y1,x2,midy);
build(son3,x1,midy+1,midx,y2);
build(son4,midx+1,midy+1,x2,y2);
}
if(x1==x2){
build(son1,x1,y1,x1,midy);
build(son3,x1,midy+1,x1,y2);
}
if(y1==y2){
build(son1,x1,y1,midx,y1);
build(son2,midx+1,y1,x2,y1);
}
update(num);
}
void getmax(int num,int x1,int y1,int x2,int y2){
if(tree[num].x1>x2||tree[num].x2<x1||tree[num].y1>y2||tree[num].y2<y1) return ;
else if(tree[num].x1>=x1&&tree[num].x2<=x2&&tree[num].y1>=y1&&tree[num].y2<=y2)
Max=max(tree[num].Max,Max);
else{
getmax(son1,x1,y1,x2,y2);
getmax(son2,x1,y1,x2,y2);
getmax(son3,x1,y1,x2,y2);
getmax(son4,x1,y1,x2,y2);
}
}
void getmin(int num,int x1,int y1,int x2,int y2){
if(tree[num].x1>x2||tree[num].x2<x1||tree[num].y1>y2||tree[num].y2<y1) return ;
else if(tree[num].x1>=x1&&tree[num].x2<=x2&&tree[num].y1>=y1&&tree[num].y2<=y2)
Min=min(tree[num].Min,Min);
else{
getmin(son1,x1,y1,x2,y2);
getmin(son2,x1,y1,x2,y2);
getmin(son3,x1,y1,x2,y2);
getmin(son4,x1,y1,x2,y2);
}
}
int main(){
getint(n),getint(k),getint(T),k--;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
getint(arr[i][j]);
build(1,1,1,n,n);
while(T--){
getint(x),getint(y),Max=-inf,Min=inf;
if(!vis[x][y]){
getmax(1,x,y,x+k,y+k),getmin(1,x,y,x+k,y+k);
ans[x][y]=Max-Min,vis[x][y]=1;
}
printf("%d\n",ans[x][y]);
}
return 0;
}
總結:總之考得非常炸非常炸非常炸非常炸非常炸非常炸非常炸非常炸非常炸非常炸233333。。。。。幾次數據結構的考試除了樹狀數組基本都是這種效果,平時做題感覺自己數據結構其實還好,但考試時,有時代碼量一大直接導致手殘概率倍增,時間也比較緊,想思路時發現需要AVL什麼的都很緊張(好長啊不想打好長啊不想打好長啊不想打好長啊不想打好長啊不想打好長啊不想打好長啊不想打好長啊不想打好長啊不想打好長啊不想打好長啊不想打好長啊不想打)處於人腦估計哪一題短就先做哪一題的糾結中。雖然不想承認,數據結構真的比我想象中水很多(尤其考試狀態下,手殘黨的絕望)