2021第十七届HNCPC(更新中)

15 篇文章 1 订阅
12 篇文章 0 订阅

A 1157 12强赛

Description
这次世预赛咱们再次打进了12强,12支队伍分为两个小组,各6支队伍,每个小组进行主客场双循环比赛,赢一场积3分,平一场积1分,输则积0分。

现在我们来写一个简单的程序,根据一个小组所有比赛的结果,输出各队的积分、净胜球总数、进球总数。一个队的净胜球为该队总进球数减去总失球数。

Input
第一行一个整数t,表示有t组测试数据。

将小组的 6 个国家编号为 A~F 6 个字母,每组数据15行,为小组的两两主客场比赛。

每行为空格隔开的两个大写字母与四个整数,表示两支队伍主客场比赛对应的进球数,A B 1 2 3 4表示AB的两场比赛比分分别为A 1 : B 2与A 3 : B 4。

进球数都为非负整数且不大于10。

Output
对于每组数据输出6行,每行3个整数,分别是A~F的积分、净胜球总数、进球总数。

Sample Input
1
B A 0 2 2 0
C A 1 2 1 2
C B 2 0 1 2
D A 2 2 1 2
D B 0 2 0 0
D C 2 1 0 1
E A 0 2 2 1
E B 2 2 0 0
E C 0 2 2 0
E D 0 2 2 1
F A 1 0 2 0
F B 2 0 0 1
F C 2 0 1 0
F D 0 2 2 1
F E 2 0 2 1

Sample Output
16 1 13
15 0 9
9 -4 9
11 -1 11
11 -5 9
24 9 14

Hint

Source
湖南省第十七届大学生计算机程序设计竞赛(HNCPC2021)

Author
CSGrandeur

简单模拟!

#include<bits/stdc++.h>
using namespace std;

int t;
const int N = 20;
typedef struct Team
{
    int score = 0;
    int js = 0;
    int j = 0;
    int s = 0;
}team;

int main()
{
    cin >> t;
    while (t--)
    {
        team T[N];//a,b,c,d,e,f
        for (int i = 1; i <= 15; ++i)
        {
            char a, b;
            int c, d, e, f;
            cin >> a >> b >> c >> d >> e >> f;
            int t1 = a - 'A' + 1;
            int t2 = b - 'A' + 1;
            if (c > d) T[t1].score += 3, T[t1].j += c, T[t2].j += d, T[t1].s += d, T[t2].s += c, T[t1].js += c - d, T[t2].js += d - c;
            else if (c == d) T[t1].score++, T[t2].score++, T[t1].j += c, T[t2].j += d, T[t1].s += d, T[t2].s += c;
            else T[t2].score += 3, T[t2].j += d, T[t1].j += c, T[t2].s += c, T[t1].s += d, T[t2].js += d - c, T[t1].js += c - d;

            if (e > f) T[t1].score += 3, T[t1].j += e, T[t2].j += f, T[t1].s += f, T[t2].s += e, T[t1].js += e - f, T[t2].js += f - e;
            else if (e == f) T[t1].score++, T[t2].score++, T[t1].j += e, T[t2].j += f, T[t1].s += f, T[t2].s += e;
            else T[t2].score += 3, T[t2].j += f, T[t1].j += e, T[t2].s += e, T[t1].s += f, T[t2].js += f - e, T[t1].js += e - f;
        }
        for (int i = 1; i <= 6; ++i)
        {
            cout << T[i].score << ' ' << T[i].js << ' ' << T[i].j << endl;
        }
    }
    return 0;
}

C 1159 Average of Two Numbers

Description
Given a list of n different numbers, which number in this list can be represented as an average of any two other numbers in the same list?

Input
There are multiple test cases.

The first line of each test case is an integer n, where 3 ≤ n ≤ 1000. The second line contains n different positive integers seperated by spaces, denoting the list of numbers. The numbers are in ascending order and every number in this list is less than 10^9.

Output
For each test case, output the number of numbers in this list which can be represented as an average of any two other numbers in the same list.

Sample Input
3
1 2 3
6
3 4 6 7 8 9

Sample Output
1
3

Hint
In the first case, 2 can be represented by (1 + 3) / 2.

In the second case, 6 can be represented by (3 + 9) / 2, 7 can be represented by (6 + 8) / 2, 8 can be represented by (7 + 9) / 2.

Source
湖南省第十七届大学生计算机程序设计竞赛(HNCPC2021)

Author
Staginner

根据等差数列的特点,在循环内构造出合适的目标值 t ,并二分搜索目标值并计数。

将时间复杂度控制在nnlogn即可通过本题。(二分复杂度logn)

#include<bits/stdc++.h>

using namespace std;
#define int long long
int n;
const int N = 1010;
int a[N];
map<int,bool> mib;

int bsearch(int lf,int t)
{
	int l = lf,r = n-1;
	while(l<r)
	{
		int mid = l+r>>1;
		if(a[mid]>=t) r = mid;
		else l = mid+1;
	}
	if(a[l]!=t) return -1;
	return l;
}

signed main()
{
	cin.tie(0),ios::sync_with_stdio(false);
	while(cin>>n)
	{
		mib.clear();
		for(int i=0;i<n;++i) cin>>a[i];
		int cnt = 0;
		for(int i=1;i<n-1;++i)
		{
			for(int j=0;j<i;++j)
			{
				int t = 2*a[i] - a[j];
				int target = bsearch(i,t);
				if(target!=-1&&!mib[a[i]]) cnt++,mib[a[i]] = true;
			}
		}
		cout<<cnt<<endl;
	}
	return 0;
}

除了手打二分函数,我们还可以利用algorithm库里面的lower/upper bound函数。

(1)lower_bound

用于查找一个有序序列指定范围内的第一个大于等于给定值的位置,返回的是指针或者迭代器,即如果是数组,找不到返回范围的尾部容器则返回end()
时间复杂度 O ( l o g n ) O(logn) O(logn)

(2) upper_bound

用于查找一个有序序列指定范围内的第一个大于给定值的位置,返回的是指针或者迭代器,即如果是数组,找不到返回范围的尾部容器则返回end()
时间复杂度 O ( l o g n ) O(logn) O(logn)

由于lower_bound是大于等于,言下之意找到的元素有可能大于也有可能等于目标值。

因此在下方代码 for 中的 if 内要特判一下(这很重要),当函数返回的迭代器指向的不是范围尾部,且其指向的元素值恰好等于要找的目标值,且当前元素a[i]没有被计算过,我们才能使cnt加一。

#include<bits/stdc++.h>

using namespace std;
#define int long long
int n;
const int N = 1010;
int a[N];
map<int,bool> mib;

signed main()
{
	cin.tie(0),ios::sync_with_stdio(false);
	while(cin>>n)
	{
		mib.clear();
		for(int i=0;i<n;++i) cin>>a[i];
		int cnt = 0;
		for(int i=1;i<n-1;++i)
		{
			for(int j=0;j<i;++j)
			{
				int t = 2*a[i] - a[j];
				int target = lower_bound(a+i+1,a+n,t) - a;
				if(a[target]==t&&target!=n&&!mib[a[i]]) cnt++,mib[a[i]] = true;
			}
		}
		cout<<cnt<<endl;
	}
	return 0;
}

D 1160 Sum Them

Description
Given n and m, calculate the sum of the incremental multiplication formulas:

在这里插入图片描述

Input
No more than 50 test cases(所以计算时间复杂度的时候记得乘上50!
). Each case one line, including integer n and m.

1 ≤ n, m ≤ 10^6.

Output
For each test case, output the result of f(n, m) modulo 10^9 + 7.

Sample Input
2 2
98 3

Sample Output
8
24497550

Hint

Source
湖南省第十七届大学生计算机程序设计竞赛(HNCPC2021)

Author
CSGrandeur

首先我们可以很容易在草稿纸上将题目所给公式变形成:
f(n,m) = 1×2×…×m + 2×3×…×(m+1) + … + n×(n+1)×…×(n+m-1)

我们发现,从第二项开始,每一项都是在前面一项的基础上先除前面一项中的第一个因子,再乘前面一项的最后一个因子加一(一共包含了n项)。

由于数据范围较大,题目很贴心地 给出了需要取得mod = 1e9 + 7(注意一下这是个质数,这是关键)

在这道题的代码书写中我们显然可以预测到可能要 在模下进行除法,而在数论中有一个常识,当我们遇到这种情况时要转换为 乘 对应除数的逆元,这样才不会出错(请记住!)

那为什么选择线性逆元呢?

根据我下面放的那篇博文,别的方法我没怎么了解,但是对于 线性求逆元 只需要 模的数是质数求逆元的数的范围在1~mod之间(可以保证两者互质) 就行,而题中mod = 1e9+7正好满足这两个条件。

这时我们又可以想到用快速幂结合费马小定理求逆元,比较而言还是线性更好,不过前者也可以做,当然了,还有我们的扩展欧几里得求逆元,这个方法就不需要模数为质数也可以做了下面给出这三种做法

(这篇博文中求逆元的方法总结的很全面,我就是在这里学了线性求逆元的做法:各种求逆元方法的汇总

做法一:用 线性求逆元 预处理 1~1e6 每个数的模 mod 逆元。

时间复杂度:O(n)(预处理逆元) + 50(No more than 50 test cases) *(O(n) + O(m)(视情况而加))

(350 ms)

#include<bits/stdc++.h>

using namespace std;
#define int long long
int n,m;
const int mod = 1e9+7,N = 1e6+10;
int inv[N];

void init()
{
    inv[0] = 0, inv[1] = 1;//初始化1的逆元为1
    for (int i = 2; i <= N; ++i)
    {
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    }
}

signed main()
{
    init();
	while(cin>>n>>m)
	{
		 int tmp = 1;
		 for(int i = 1;i<=m;++i) tmp = tmp*i%mod;
		 int p = 1;
		 int sum = tmp;
		 for(int i = 2;i<=n;++i)
		 {
		     tmp = tmp * inv[p] % mod * (m+p) % mod;
		 	 sum = (sum + tmp)%mod;
		 	 p++;
		 }
		 cout<<sum<<endl;
	}
	return 0;
}

做法二:用 快速幂求逆元 预处理 1~1e6 每个数的模 mod 逆元。

时间复杂度:O(nlogp)(预处理逆元) + 50(No more than 50 test cases) *(O(n) + O(m)(视情况而加))

(436 ms,如果 不预处理,而是直接在循环内部快速幂求逆元的话会 超时 (时间复杂度:50 * O(nlogp)))

#pragma GCC optimize(2)
#include<bits/stdc++.h>

using namespace std;
#define int long long
int n,m;
const int mod = 1e9+7,N = 1e6+10;
int inv[N];

inline int qmi(int a,int b)
{
        int res = 1;
        while(b)
        {
                if(b&1) res = res * a % mod;
                b>>=1;
                a = a * a % mod;
        }
        return res;
}

inline void init()
{
        for(int i=1;i<N;++i) inv[i] = qmi(i,mod-2);
}

signed main()
{
    init();
	while(cin>>n>>m)
	{
		 int tmp = 1;
		 for(int i = 1;i<=m;++i) tmp = tmp * i % mod;
		 int p = 1;
		 int sum = tmp;
		 for(int i = 2;i<=n;++i)
		 {
		     tmp = tmp * inv[p] % mod * (m+p) % mod;
		 	 sum = (sum + tmp) % mod;
		 	 p++;
		 }
		 cout<<sum<<endl;
	}

	return 0;
}

做法三:用 扩展欧几里得 预处理 1~1e6 每个数的模 mod 逆元。

时间复杂度:O(nlog(ai+p))(预处理逆元) + 50(No more than 50 test cases) *(O(n) + O(m)(视情况而加))

(520 ms)

#pragma GCC optimize(2)
#include<bits/stdc++.h>

using namespace std;
#define int long long
const int mod = 1e9+7,N = 1e6+10;
int inv[N];
int n,m;

inline void exgcd(int a,int p,int &d,int &x,int &y)
{
        if(!p)
        {
                d = a,x = 1,y = 0;
                return ;
        }
        exgcd(p,a%p,d,y,x),y-=a/p*x;
}

inline void init()
{
        int d,x,y;
        for(int i=1;i<N;++i) exgcd(i,mod,d,x,y),inv[i] = (x+mod)%mod;
}

signed main()
{
    init();
	while(cin>>n>>m)
	{
		 int tmp = 1;
		 for(int i = 1;i<=m;++i) tmp = tmp * i % mod;
		 int p = 1;
		 int sum = tmp;
		 for(int i = 2;i<=n;++i)
		 {
		     tmp = tmp * inv[p] % mod * (m+p) % mod;
		 	 sum = (sum + tmp) % mod;
		 	 p++;
		 }
		 cout<<sum<<endl;
	}
	return 0;
}

补充官方题解

做法四:先预处理 n! ,再用 线性求逆 预处理 1~1e6 每个数的模 mod 逆元。(当然,快速幂、扩欧 也都是可以的,只不过效率没这么高)

时间复杂度:O(n + n)(预处理 阶乘 和 逆元) + 50(No more than 50 test cases) * O(n)

(248 ms)

#pragma GCC optimize(2)
#include<bits/stdc++.h>

using namespace std;
#define int long long
int n,m;
const int mod = 1e9+7,N = 1e6+10;
int inv[N],fac[N];

inline void init1()
{
    inv[0] = 0, inv[1] = 1;//初始化1的逆元为1
    for (int i = 2; i <= N; ++i)
    {
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    }
}

inline void init2()
{
        fac[0] = 1;
        for(int i=1;i<N;++i) fac[i] = fac[i-1] * i % mod;
}

signed main()
{
        init1(), init2();
        while(cin>>n>>m)
	    {
		 	int tmp, sum, p;
		 	p = 1, tmp = sum = fac[m];

		 	for(int i=2;i<=n;++i)
		 	{
		    	tmp = tmp * inv[p] % mod * (m+p) % mod;
		 	    sum = (sum + tmp) % mod;
		 	    p++;
		 	}
	   		cout<<sum<<endl;
	    }

	    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值