考试(2017年3月26日) (COCI)

第一题:银行账户(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内可以接受。
【输入样例】
2
100 200
【输出样例】
50.0
66.66666666666666
 
水题一道,就是注意 int 会爆就ok
直接上代码:
    #include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
 
const int MAXN=300000;
using namespace std;
 
struct node{double data;}ac[MAXN+5];
int n;
double temp,k,a1,a2;
long long sum;
 
bool cmp(node t1,node t2){return t1.data>t2.data;}
 
int main()
{
    freopen("account.in","r",stdin);
    freopen("account.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lf",&ac[i].data);
        sum+=ac[i].data;
    }
    sort(ac+1,ac+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        k+=ac[i].data;
        double t1=100*k/sum,t2=100.0*i/n;
        if(temp<t1-t2)
        {
            a1=t1;
            a2=t2;
        temp=t1-t2;
        }
        else break;
    }
    printf("%lf",a2); putchar(10);
    printf("%lf",a1); putchar(10);
}
 
 
第二题:染色面积(area)
【题目描述】给你N个矩形,这些矩形在平面坐标系中,并且以坐标系的零点为中心,它们的边都平行于坐标轴。每个矩形由宽度和高度可以唯一确定。现在对矩形进行染色操作。下图即是样例的示意图:
  
 

现在请你计算有染色区域的面积。
输入格式:
第一行包含1个整数N(1<=n<=1000000),表示矩形的个数。
接下来N行包含两个偶数X和Y(2<=X,Y<=10000000),分别表示宽度和高度。
输出格式:
一行,表示区域面积。
  
【输入样例】
3
8 2
4 4
2 6
  
【输出样例】
28
 
这道题也水,我旁边那位童鞋乍一看以为是矩形树,但仔细再想想就不对……
按y值从大到小排个序,也不用管什么这些矩形在平面坐标系中,并且以坐标系的零点为中心
就当他给的是一象限的x,y值
还有也是要注意int会爆,而且不只是答案,在计算答案的中间过程时也会爆(WA得我好爽)
 
主要思路是这样的:
按y值从大到小排序后,枚举y值
比较相邻的x值
设y值大者坐标为a(x1,y1),小者为b(x2,y2),S表示之前已覆盖的面积
显然y1 > y2
x分两种情况
(1)  x1>=x2
显然b所覆盖的面积已被a覆盖,跳过这种情况即可
(2)  x2>x1
此时,面积为:S+=(x2-x1)*y2;
 
代码如下:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
const int MAXN=1000000;
using namespace std;
struct POINT{int x,y;}point[MAXN+5];
int n,t;
unsigned long long ans;
bool cmp(POINT a,POINT b){return a.y>b.y;}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&point[i].x,&point[i].y);
    sort(point+1,point+n+1,cmp);
    t=point[1].x; ans=point[1].x*point[1].y;
    for(int i=2;i<=n;i++)
    {
        if(t<point[i].x)
        {
            ans+=(long long)((point[i].x-t))*point[i].y;
            t=point[i].x;
        }
    }
    printf("%I64d",ans);
}
好了,接下了两道题,我一道迷迷糊糊,一道完全没搞懂
对于接下来两道题,对于完全没搞懂的那道我会直接上标程和官方题解(标程我编译都会出毒,题解不知所云,有大神看懂了或知道怎么做还请指点下我)
 
第三题:航班(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
【输入样例1】
2
0
  
【输出样例1】
DA
  
【输入样例2】
3
2
1 2
2 3
【输出样例2】
NE
  
【输入样例3】
4
2
1 3
2 4
【输出样例3】
DA
 
这道题有一个结论(不要问我怎么推的,猜了好多组数据+看了懵逼的题解总结出来的,如有错误很正常,也请指正)
就是如果能够形成完全图
当且仅当:
(1) 图中完全图数量不大于2
(2) 无边相连的点的个数不多于2
 
按着这思路写就莫名其妙的A了
 
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
  
const int MAXN=1000;
const int MAXM=500000;
using namespace std;
struct node{int e,next;}h[MAXM*2];
int Empty,Count,num,point,n,m,a,b,cnt,fir[MAXN+5];
bool used[MAXN+5][MAXN+5],vis[MAXN+5];
  
void dfs(int s){
    vis[s]=1; point++;
    for(int i=fir[s];i;i=h[i].next)
    {
        if(!used[s][h[i].e])
        {
            num++;
            used[s][h[i].e]=1;
            used[h[i].e][s]=1;
        }
        if(!vis[h[i].e]) dfs(h[i].e);
    }
}
int main()
{
    freopen("airline.in","r",stdin);
    freopen("airline.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        h[++cnt].e=b;
        h[cnt].next=fir[a];
        fir[a]=cnt;
        h[++cnt].e=a;
        h[cnt].next=fir[b];
        fir[b]=cnt;
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            dfs(i);
            if(point!=1) Count++;
            else Empty++;
            if(point*(point-1)/2!=num)
            {
                putchar('N'); putchar('E');
                putchar(10);
                return 0;
            }
            num=0; point=0;
        }
    }
    if(Empty>2)
    {
        putchar('N'); putchar('E');
        putchar(10);
    }
    else if(Count<=2)
    {
        putchar('D'); putchar('A');
        putchar(10);
    }
    else
    {
        putchar('N'); putchar('E');
        putchar(10);
    }
}

第四题:数组询问(query

【题目描述】Mirko是一个非常单纯的人,他的好朋友给他一个有N个自然数的数组,然后对他进行Q次查询.

每一次查询包含两个正整数L,R,表示一个数组中的一个区间[L,R]Mirko需要回答在这个区间中有多少个值刚好出现2次。

【输入格式】

第一行包含两个整数NQ(1<=N,Q<=500000)

第二行包含N个自然数,这些数均小于1000000000,表示数组中的元素。

接下来有Q行,每行包含两个整数LR(1<=L<=R<=N)

【输出格式】

输出包含Q行,每行包含一次查询的答案。

【输入样例1

5 1

1 2 1 1 1

1 3

 

【输出样例1

1

 

【输入样例2

5 2

1 1 1 1 1

2 4

2 3

【输出样例2

0

1



下面那一道就是题解标程都看不懂的题了:
#include <cstdio>
#include <string>
#include <vector>
#include <map>
#include <cstdlib>
#include <algorithm>
#include <cstring>


using namespace std;


typedef long long llint;
typedef pair <int, int> pii;


const int MAXN = 1 << 20;


int n, q;


vector <pii> T[MAXN];
int a[MAXN];
int ls[MAXN], rs[MAXN];
int left[MAXN];
int right[MAXN];
int ans[MAXN];


void update (int x, int l, int r, int L, int v) {
if (L > r || L < l) return;
T[x].push_back({rs[v], v});
int mid = (l + r) / 2;
if (l == r) return;
update(x * 2, l, mid, L, v);
update(x * 2 + 1, mid+1, r, L, v);
}


void query_update (int x, int l, int r, int L, int R, int p1, int p2) {
if (L > r || R < l) return;
if (l >= L && r <= R) {
T[x].push_back({p1, -1});
T[x].push_back({p2, -2});
return;
}
int mid = (l + r) / 2;
query_update(x * 2, l, mid, L, R, p1, p2);
query_update(x * 2 + 1, mid+1, r, L, R, p1, p2);
}


void solve (int x, int l, int r) {
sort(T[x].begin(), T[x].end());
int cnt = 0;
for (auto v: T[x]) {
if (v.second == -1) ++cnt;
else if (v.second == -2) --cnt;
else ans[v.second] += cnt;
}
if (l == r) return;
int mid = (l+r) / 2;
solve(x*2, l, mid);
solve(x*2+1, mid+1, r);
}


int main (void){
scanf("%d%d", &n, &q);
for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
for (int i = 0; i < q; ++i) {
scanf("%d%d", &ls[i], &rs[i]);
--ls[i]; --rs[i];
update(1, 0, n-1, ls[i], i);
}
map <int, int> M;
for (int i = 0; i < n; ++i) {
int pos = -1;
if (M.count(a[i])) pos = M[a[i]];
left[i] = pos;
M[a[i]] = i;
}
M.clear();
for (int i = n-1; i >= 0; --i) {
int pos = n;
if (M.count(a[i])) pos = M[a[i]];
right[i] = pos;
M[a[i]] = i;
}
right[n] = n;


for (int i = 0; i < n; ++i) {
int rpos = right[i];
if (rpos == n) continue;
query_update(1, 0, n-1, left[i] + 1, i, rpos, right[rpos]);
}


solve(1, 0, n-1);
for (int i = 0; i < q; ++i)
printf("%d\n", ans[i]);


return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值