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)最后总结:一定要检查一定要检查一定要检查一定要检查一定要检查一定要检查一定要检查一定要检查一定要检查一定要检查