2017年3月26日考試總結
第一題:銀行賬戶(account)
【題目描述】大家都知道28定律吧,據說世界上20%的人擁有80%的財富。現在你對一家銀行的賬戶進行檢測,看是否符合28定律,或者有更強的定律。比如說,10%的人擁有85%的財富。更準確的描述是:對N個銀行賬戶進行調查,你的任務是找出兩個數A,B,使得B-A的差最大。A,B的含義是A%的人擁有B%的財富。
【輸入格式】
輸入的第一行包含一個整數N(1<=N<=300000),表示銀行賬戶的個數。
接下來一行包含N個整數,每個整數在區間[0,100000000],表示這N個賬戶中的存款金額。
【輸出格式】
輸出兩行,分別是兩個實數A,B。A,B的含義如題所述。誤差在0.01內可以接受。
成績:100(AC)
題解:把存款金額排序,在從大到小枚舉,取最大值即可
分析:大水題
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
using namespace std;
const int N=300000+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;
}
int n,arr[N];
long long all;
double A,B=-1.0,sum,num,a,b;
int main(){
freopen("account.in","r",stdin);
freopen("account.out","w",stdout);
getint(n);
for(int i=1;i<=n;i++)
getint(arr[i]),all+=arr[i];
sort(arr+1,arr+n+1);
for(int i=n;i>=1;i--){
num+=1.0,sum+=arr[i];
a=num/n,b=sum/all;
if(b-a>B-A) A=a,B=b;
}
A*=100.0,B*=100.0;
printf("%.20lf\n%.20lf\n",A,B);
return 0;
}
第二題第二題:染色面積(area)
【題目描述】給你N個矩形,這些矩形在平面座標系中,並且以座標系的零點爲中心,它們的邊都平行於座標軸。每個矩形由寬度和高度可以唯一確定。現在對矩形進行染色操作。
現在請你計算有染色區域的面積。
輸入格式:
第一行包含1個整數N(1<=n<=1000000),表示矩形的個數。
接下來N行包含兩個偶數X和Y(2<=X,Y<=10000000),分別表示寬度和高度。
輸出格式:
一行,表示區域面積。
成績:100(AC)
題解:按y從大到小排序,去掉完全包含的情況後,ans+=arr[i].y*(arr[i].x-arr[i-1].x);
分析:思路很好想,然而各種莫名其妙的東西特別多,做的時候也耽誤了不少時間,寫了很多版本,最後寫了個暴力對拍了1個多小時,才放心。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1000000+10;
inline void getLL(long long&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{
long long x,y;
bool operator < (const node &a)const{
if(y==a.y) return x>a.x;
else return y>a.y;
}
}arr[N];
long long n,ans,Max;
int main(){
freopen("area.in","r",stdin);
freopen("area.out","w",stdout);
getLL(n);
for(int i=1;i<=n;i++)
getLL(arr[i].x),getLL(arr[i].y);
sort(arr+1,arr+n+1);
for(int i=1;i<=n;i++){
if(arr[i].x<=Max){
arr[i]=arr[i-1];continue ;
}
Max=max(Max,arr[i].x);
ans+=arr[i].y*(arr[i].x-arr[i-1].x);
}
printf("%I64d\n",ans);
return 0;
}
第三題:航班(airline)
【題目描述】有N個城市,它們之間都有雙向的航線。一個瘋狂的航空公司老闆經常改變航班日程。每天他都會做以下的事情:
1.選擇一個城市
2.從該城市出發沒有航線到達的城市全部開通航線,同時將之前開通的從該城市出發的所有航線全部取消
舉個例子,比如從城市5出發,可以達到城市1和城市2,不能到達城市3和城市4,老闆選擇城市5做出改變後,那麼城市5就有航班可以到達城市3和城市4,同時沒有航班到達城市1和城市2了。
市民們想知道有沒有一天,航線形成一個完全圖。即每一個城市都有到達其他所有城市的航線,或者永遠不可能形成一個完全圖,不管老闆如何操作。寫一個程序來判斷
【輸入格式】
第一行包含一個整數N(2<=N<=1000),表示城市的數量。城市的編號從1到N
第二行包含一個整數M(0<=M<=N*(N-1)/2),表示當前航班的數量。
接下來又M行,每行包含兩個不同的整數,A,B,表示A,B兩個城市有航線。
【輸出格式】
有且只有一行,如果能夠形成完全圖,則輸出DA,如果不能形成完全圖,則輸出NE
成績:57
題解:DA只有三種情況:1.兩個以內獨立的點
2.一個多餘兩個點的完全圖加上多個獨立的點
3.兩個完全圖,沒有獨立的點
分析:我打掉了一句話(我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz)
加上一句話我就AC了(絕望絕望絕望絕望絕望絕望絕望絕望絕望絕望絕望絕望絕望絕望)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1000+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;
}
int n,m,a,b,way[N],cnt;
bool map[N][N],fa[N],vis[N],flag,flg;
void dfs(int x){
vis[x]=1,way[++cnt]=x;
for(int i=1;i<=n;i++)
if(map[x][i]&&!vis[i])
dfs(i);
}
bool check(int x){
cnt=0,dfs(x);
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
if(!map[way[i]][way[j]])
return 0;
return 1;
}
int main(){
freopen("airline.in","r",stdin);
freopen("airline.out","w",stdout);
getint(n),getint(m);
if(m>=n*(n-1)/2){
printf("DA\n");return 0;
}
for(int i=1;i<=m;i++){
getint(a),getint(b),fa[a]=fa[b]=1;
map[a][b]=map[b][a]=1;
}
for(int i=1;i<=n;i++){
map[i][i]=1;
if(!fa[i])flag=1;
else flg=1;
}
if(flag){
if(!flg&&n>2){
printf("NE\n");return 0;
}
for(int i=1;i<=n;i++){
if(!fa[i]) continue ;
for(int j=1;j<=n;j++)
if(!map[i][j]&&fa[j]){
printf("NE\n");return 0;
}
}
}
else{
if(!check(1)){
printf("NE\n");return 0;
}
flg=0;//就是這句掉了(我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz)
for(int i=1;i<=n;i++)
if(!vis[i]){
if(flg||!check(i)){
printf("NE\n");return 0;
}flg=1;
}
}
printf("DA\n");
return 0;
}
第四題:數組詢問(query)
【題目描述】Mirko是一個非常單純的人,他的好朋友給他一個有N個自然數的數組,然後對他進行Q次查詢.
每一次查詢包含兩個正整數L,R,表示一個數組中的一個區間[L,R],Mirko需要回答在這個區間中有多少個值剛好出現2次。
【輸入格式】
第一行包含兩個整數N和Q(1<=N,Q<=500000)
第二行包含N個自然數,這些數均小於1000000000,表示數組中的元素。
接下來有Q行,每行包含兩個整數L和R(1<=L<=R<=N)。
【輸出格式】
輸出包含Q行,每行包含一次查詢的答案。
成績:40
題解:從小到大,處理統一回答同一個r的答案,(一個元素如果要跟新[l, r]的答案,那麼,在[l, r]內,這個元素就只能出現兩次,所以,對此,我們可以知道對於一個固定的r, 和一個在[1, r]內移動的l,如果在區間內的一個元素,我們只需要在這個元素的第二次出現的前一個位置+1,後一個位置-1就行了。樹狀數組維護1~r)
分析:卡了很久,最後交了個O(n^2)暴力,也算是盡力把能騙到的分都騙了233333
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
const int N=500000+10;
const int inf=1000000001;
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 num,pos,w;
bool operator < (const node &x)const{
return num<x.num;
}
}arr[N];
struct nod{
int l,i;
nod(){}
nod(int a,int b){
l=a,i=b;
}
};
vector <nod> q[N];
int n,T,cnt,L,R,pos[N][3],tree[N],ans[N];
bool flag;
bool cmp(node a,node b){
return a.pos<b.pos;
}
int lowbit(int x){
return x&-x;
}
void update(int pos,int val){
if(pos<0) return ;
while(pos<=n)
tree[pos]+=val,pos+=lowbit(pos);
}
int getsum(int pos){
int res=0;
while(pos>0)
res+=tree[pos],pos-=lowbit(pos);
return res;
}
void Delele(int x){
if(pos[arr[x].w][0]==-1) return ;
update(pos[arr[x].w][0]+1,-1);
update(pos[arr[x].w][1]+1,1);
}
void Insert(int x){
if(pos[arr[x].w][0]==-1) return ;
update(pos[arr[x].w][0]+1,1);
update(pos[arr[x].w][1]+1,-1);
}
void maintain(int x){
pos[arr[x].w][0]=pos[arr[x].w][1];
pos[arr[x].w][1]=pos[arr[x].w][2];
pos[arr[x].w][2]=x;
}
int main(){
getint(n),getint(T);
for(int i=1;i<=n;i++)
getint(arr[i].num),arr[i].pos=i;
sort(arr+1,arr+n+1),arr[0].num=inf;
for(int i=1;i<=n;i++){
if(arr[i].num!=arr[i-1].num)
arr[i].w=++cnt;
else arr[i].w=cnt;
}
sort(arr+1,arr+n+1,cmp);
for(int i=1;i<=cnt;i++)
pos[i][0]=pos[i][1]=-1;
for(int i=1;i<=T;i++){
getint(L),getint(R);
q[R].push_back(nod(L,i));
}
for(int i=1;i<=n;i++){
Delele(i);
maintain(i);
Insert(i);
int len=q[i].size();
for(int j=0;j<len;j++)
ans[q[i][j].i]=getsum(q[i][j].l);
}
for(int i=1;i<=T;i++)
printf("%d\n",ans[i]);
return 0;
}
總結:如果第三題不手殘的話,這次考試基本可以說沒有遺憾了(第四題考試時的思路跟正解沒有任何關係),然而第三題錯得我想掐死自己23333(我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz我是zz)(檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊檢查啊)(233333)最後總結:一定要檢查一定要檢查一定要檢查一定要檢查一定要檢查一定要檢查一定要檢查一定要檢查一定要檢查一定要檢查