2019ccpc东北地区大学生程序设计竞赛(东北电力大学)前四题

2019ccpc东北地区大学生程序设计竞赛(东北电力大学)前四题


本人是个ACM菜鸡选手,写个博客记录下自己第一次带队的经历。大佬勿喷!
现场赛只做出J,G,C三题,比赛结束看了大佬的B题代码。其他题也没读过了,就写这四题题解吧
两位队友不可或缺,有他们才能稳住。

我的个人博客


J.

题意:计算限制时间x,规则如下:

  1. x ≥ 3 a 1 a1 a1.
  2. x ≥ a i ai ai+1,i∈[2,n].
  3. x是最小的偶数.
    Input:
    第一行是一个整数T(1 ≤ T ≤ 10)表示测试数量
    每个测试事例第一行有一个整数n(2 ≤ n ≤10)
    第二行,有n个整数 a 1 , a 2 , . . . , a n a1,a2,...,an a1,a2,...,an(1 ≤ a i ai ai ≤ 10)
    Output:
    输出x

一开始没看到偶数!!

input
2
2
1 3
2
1 4
output
4
6
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 20+10;
int a[maxn];
int main(){
    int t;
    cin >> t;
    while(t--){
        int n ;cin >> n;
        for(int i = 1;i <= n;i++) cin >> a[i];
        int ma = 0;
        for(int i = 2;i <= n;i++){
            if(a[i] > ma) ma = a[i];
        }
        ma++;
        int ans = max(3*a[1],ma);
        if(ans&1) ans++;
        cout << ans << endl;
    }
    return 0;
}

G.

题意:给你n个矩形,移动这n个矩形使得存在一个方块覆盖了所有矩形。
第i个矩形的左下角方格 ( a i , b i ) (ai,bi) aibi,右上角方格 ( c i , d i ) (ci,di) cidi
Input:
第一行是一个整数T(1 ≤ T ≤ 1000)表示测试数量
每个测试事例第一行有一个整数n(1 ≤ n ≤100000)表示矩形数量
接下来n行,每行包含4个整数 a i , b i , c i , d i ( 1 ≤ a i , b i , c i , d i ≤ 1 0 9 , a i ≤ c i , b i ≤ d i ) . Σ n ≤ 1 0 6 ai,bi,ci,di(1 ≤ ai,bi,ci,di ≤ 10^9,ai ≤ ci,bi ≤ di).Σn ≤ 10^6 aibicidi1aibicidi109,aicibidi.Σn106
Output:
输出最小移动步数

思路:货仓选址问题:在一条数轴上有n家商店,求把货仓建在何处,使得货仓到每家商店的距离之和最小。设货仓建在x坐标处,x左侧有P家商店,右侧有Q家商店。若P<Q,则每把货仓的选址向右移动1单位距离,距离之后就会变小Q-P。同理,P>Q,则货仓的选址向左移动会使距离之和变小。当P=Q时为最优解。及建在中位数处。当n为奇数,货仓建在A[(n+1)/2]处最优,当n为偶数时,货仓建在A[n/2]~A[n/2+1]之间的任何位置都是最优解。
此题是二维的货仓选址,只需将x和y分别取中位数即可。

队友说移到中间位置,提醒了我中位数。

input
1
2
2 2 3 3
4 4 5 5
output
2
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
struct node{
    int x1 = 0;
    int y1 = 0;
    int x2 = 0;
    int y2 = 0;
}a[maxn];
int x[maxn*2],y[maxn*2],tot;
int main(){
    int t;
    cin >> t;
    while(t--){
        int n ;cin >> n;
        tot = 0;
        for(int i = 1;i <= n;i++){
            cin >> a[i].x1 >> a[i].y1 >> a[i].x2 >> a[i].y2;
            x[++tot] = a[i].x1;
            y[tot] = a[i].y1;
            x[++tot] = a[i].x2;
            y[tot] = a[i].y2;
        }
        sort(x+1,x+2*n+1);
        sort(y+1,y+2*n+1);
        int xx = x[n];
        int yy = y[n];
        LL ans = 0;
        for(int i = 1;i <= n;i++){
            if(a[i].x1 < xx && a[i].x2 < xx) ans += xx-max(a[i].x1,a[i].x2);//题目规定了x1<x2,不用我这么麻烦的判断和max
            else if(a[i].x1 > xx && a[i].x2 > xx) ans += min(a[i].x1,a[i].x2)-xx;
            if(a[i].y1 < yy && a[i].y2 < yy) ans += yy-max(a[i].y1,a[i].y2);
            else if(a[i].y1 > yy && a[i].y2 > yy) ans += min(a[i].y1,a[i].y2)-yy;
        }
        cout << ans << endl;
    }
    return 0;
}

C.

题意:给你n条直线。计算有多少对(i,j),(1 ≤ i < j ≤ n),li,lj至少有一个公共点,重叠的线也是。
Input:
第一行是一个整数T(1 ≤ T ≤ 1000)表示测试数量
每个测试事例第一行有一个整数n(1 ≤ n ≤100000)表示直线数量
接下来n行,每行包含4个整数 x a i , y a i , x b i , y b i ( ∣ x a i ∣ , ∣ y a i ∣ , ∣ x b i ∣ , ∣ y b i ∣ ≤ 1 0 9 ) xai,yai,xbi,ybi(|xai|,|yai|,|xbi|,|ybi| ≤ 10^9) xaiyaixbiybixaiyaixbiybi109保证 ( x a i , y a i ) ( x b i , y b i ) (xai,yai)(xbi,ybi) xaiyaixbiybi不重点. Σ n ≤ 1 0 6 Σn ≤ 10^6 Σn106
Output:
输出有多少对直线有公共点

思路:当斜率不同时(斜率不存在定义其斜率为INF,因为点都是整数,斜率最大也不会超过 1 0 9 10^9 109,以下说斜率不同也包括斜率不存在的直线),必有交点,计算第i个直线时,只需加上前i-1条中有多少条与其斜率不同。斜率相同时,平行线无交点,重叠线两两相交,数量是num*(num-1)/2.

当时也是没想清楚,直接写,后来STL用了五个。TLE加WA了6次。慢慢理清思路,优化数据结构终于过了。还是不太稳啊,太急了。当时过了这题排名就上升了2名,罚时太多了。不过当时过了这题还是很开心的。

input
3
2
0 0 1 1
0 1 1 0
2
0 0 0 1
1 0 1 1
2
0 0 1 1
0 0 1 1
output
1
0
1
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x7fffffff;
const int maxn = 1e5+10;
struct node{
    int x1 = 0;
    int y1 = 0;
    int x2 = 0;
    int y2 = 0;
}a[maxn];
map<double,LL > num;
map<double,map<double,LL > > same_num;
int ccc[maxn];
int main(){
    int t;
    cin >> t;
    while(t--){
        int n ;
        scanf("%d",&n);
        LL ans = 0;
        num.clear();
        same_num.clear();
        for(int i = 1;i <= n;i++) {
            scanf("%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);
            if(a[i].x1 == a[i].x2) {
                ans += (i-1)-num[INF];
                num[INF]++;
                same_num[INF][a[i].x1]++;
                continue;
            }
            double xie = (a[i].y2-a[i].y1)*1.0/(a[i].x2-a[i].x1);
            if(num[xie]){
                ans += (i-1)-num[xie];
            }else ans += (i-1);
            double c = a[i].y1*1.0-xie*a[i].x1;
            num[xie]++;
            same_num[xie][c]++;
        }
        auto i = same_num.begin();
        auto j = same_num.end();
        for(;i != j;i++){
            auto k = i->second.begin();
            auto e = i->second.end();
            for(;k != e;k++){
                ans += k->second*1LL*(k->second-1)/2;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

B.

题意:有n个糖果,m种类型,每个糖果有一个价值。从n个糖果中挑出一些糖果,S是你挑出的糖果的总价值,C是你挑出的糖果中某个类型最多的那个糖果的数量。求S/C最大。
Input:
第一行是一个整数T(1 ≤ T ≤ 1000)表示测试数量
每个测试事例第一行有两个整数n,m(1 ≤ n,m ≤100000)糖果总数和类型数
第二行有m个整数, l 1 , l 2 , . . . , l m ( 1 ≤ l i ≤ n ) l1,l2,...,lm(1 ≤ li ≤ n) l1l2...,lm1lin
接下来n行,每行包含2个整数 a i , b i ( a i ≤ 1 0 8 , 1 ≤ b i ≤ m ) ai,bi(ai ≤ 10^8,1 ≤ bi ≤ m) aibiai1081bim. Σ n ≤ 1 0 6 , Σ m ≤ 1 0 6 Σn ≤ 10^6,Σm ≤ 10^6 Σn106Σm106
Output:
输出S/C,要求gcd(S,C) = 1

思路:首先选同一种类型的糖果一定是价值大的要优,所以先按价值排序。预处理出类型数量都是i时的最大价值,然后从类型数量小的枚举,累计价值(因为C是选出的类型数最大的那个数,类型数量小的直接累计)更新最大的S/C。

这题是队友从大佬那拍来的代码,应该是长春理工大学的。我们第二题同时读了B和C,我先写完B样例不对,C一开始队友都读成了线段,除了 O ( n 2 ) O(n^2) O(n2)的算法实在不会了。然后看到G有人过了,直接去看了G题。G还是比较顺利(除了一开始数组开小了,WA一次)。C提示了直线,搞啊搞就搞出了C。B做了两小时也没做出来。还是太菜了。膜拜大佬。B是别人的代码,几乎没改动。

input
2
2 1
2
7 1
2 1
3 2
1 2
2 1
5 2
3 2
output
9/2
5/1
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pii;
const int maxn = 1e5+10;
pii a[maxn];
LL l[maxn],b[maxn],c[maxn];//b[i]记当前i类型的糖果数,c[i]表示i类型选l[i]-1个的最大价值
LL f[maxn];//f[i]表示类型数量都是i时的总价值
bool isgreater(pii a,pii b){
	return a.first * b.second > a.second * b.first;
}
LL gcd(LL a,LL b){
	return b == 0?a:gcd(b,a%b);
}
int main() {
	int t;cin  >> t;
	while(t--){
		int n,m;cin >> n >> m;
		for(int i = 1;i <= m;i++) cin >> l[i];
		for(int i = 1;i <= m;i++) b[i] = c[i] = 0;
		for(int i = 1;i <= n;i++) f[i] = 0;
		for(int i = 1;i <= n;i++) cin >> a[i].first >> a[i].second;
		sort(a+1,a+n+1);
		int t;
		for(int i = n;i >= 1;i--){
			t = a[i].second;
			b[t]++;
			if(b[t] < l[t]) c[t] += a[i].first;
			if(b[t] == l[t]) f[b[t]] += c[t];
			if(b[t] >= l[t]) f[b[t]] += a[i].first;
		}
		pii ans = make_pair(0,1);
		LL z = 0;
		for(int i = 1;i <= n;i++){
			z += f[i];
			if(isgreater(make_pair(z,i),ans)) ans = make_pair(z,i);
		}
		LL g = gcd(ans.first,ans.second);
		printf("%lld/%lld\n",ans.first/g,ans.second/g);
	}
	return 0;
}

这次比赛也是达到了目标,拿了个银牌。第一次作为主敲代码手体验了现场赛的氛围,还是有点紧张的。队友之间配合的不错,感谢我的两位队友。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值