牛客练习赛69

时间复杂度
链接:https://ac.nowcoder.com/acm/contest/7329/A
来源:牛客网

题目描述
DK 想出一道超级没有素质的题

DK 给了你一个标准的时钟,初始时间在 12:00

每分钟分针会顺时针转动 6°,而时针会顺时针转动 0.5°

DK 想知道,t 分钟后,时针和分针的较小夹角的大小是多少

由于这题超级没有素质,所以你需要将答案四舍五入到整数

输入描述:
第一行一个整数 n,表示数据组数

第 2 ~ n+1行,每行一个整数 t,意义见题目描述
输出描述:
输出n行,每行一个整数,表示答案
示例1
输入
复制
2
180
360
输出
复制
90
180
备注:
1\le n\le1001≤n≤100,0\le t < 7200≤t<720

夹角最小,考虑他们的夹角和补角,要注意四舍五入

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
#include <ext/rope>
//#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(int out = 0)
{
    char c;
    while((c=getchar()) < 48 || c > 57);
    while(c >= 48 && c <= 57) out=out*10+c-48,c=getchar();
    return out; 
}

const int N = 1e5 + 10;
const int M = N * 10;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

signed main(){
	int T;
	scanf("%lld", &T);
	
	while(T --){
        int t;
        cin >> t;
		int temp1 = t % 60, temp2 = t;
		
		double ans1 = (double)temp1 * 6.0, ans2 = (double)temp2 * 0.5;
		
		int ans3 =  (int)(fabs(ans1 - ans2) + 0.5);
		int ans4 = (int)(360 - fabs(ans1 - ans2) + 0.5);
		cout << min (ans3, ans4)<< endl;
	}
	
	return 0;
}

划分
链接:https://ac.nowcoder.com/acm/contest/7329/B
来源:牛客网

给你一个长度为 n 的序列,第 i 个数为 a_ia
i

将这个序列分割成 i 个不重合的子串,从每个子串中取出最大的 j 个数作为这个分割方法的价值,记价值最大的分割方法的价值为 val(i,j)

但是金发少女 DK 觉得这太好算了,于是她要你求下面的柿子
\sum_{i=1}{x}\sum_{j=1}{y}val(i,j)∑
i=1
x


j=1
y

val(i,j)
输入描述:
第一行输入一个正整数 n

第二行输入 n 个正整数,第 i 个数表示 a_ia
i

第三行输入两个正整数 x,y,含义如题中所示

输出描述:
一个数,表示答案
示例1
输入
复制
5
6 4 4 5 3
2 2
输出
复制
47
说明
val(1,1)=6 ,分割成 [6,4,4,5,3],最大的数是 6
val(1,2)=11,分割成 [6,4,4,5,3],最大的两个数是 6 和 5
val(2,1)=11,分割成 [6,4][4,5,3],两组中各自最大的数是 6 和 5
val(2,2)=19,分割成 [6,4][4,5,3],两组中各自最大的两个数是 6,4 和 5,4
故答案为6+11+11+19=47
备注:
1\le n\le 10^51≤n≤10
5
, 1\le a_i\le 10^91≤a
i

≤10
9
, x\times y<=nx×y<=n

考虑每个数的贡献度,如果选出x个数,肯定选出最大的x个数,维护一个最大的x个数的和,然后枚举此时选出多少个数,累加即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
#include <ext/rope>
//#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(int out = 0)
{
    char c;
    while((c=getchar()) < 48 || c > 57);
    while(c >= 48 && c <= 57) out=out*10+c-48,c=getchar();
    return out; 
}

const int N = 1e5 + 10;
const int M = N * 10;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int a[N];
int n;
int s[N];

signed main(){
	scanf("%lld", &n);
	for (int i = 1; i <= n; i ++)    scanf("%lld", &a[i]);
	
	sort(a + 1, a + 1 + n);
	
	for (int i = n, j = 1; i >= 1; i --, j ++){
		s[j] = s[j - 1] + a[i];
	}
	
	int x, y;
	scanf("%lld%lld", &x, &y);
	
	int ans = 0;
	for (int i = 1; i <= x; i ++){
		for (int j = 1; j <= y; j ++){
			ans += s[i * j];
		}
	}
	
	cout << ans << endl;
	
	return 0;
}

旅行
链接:https://ac.nowcoder.com/acm/contest/7329/C
来源:牛客网

题目描述
DK 有一个无向图 G,这个无向图有 n 个点 m 条边
你需要确定一个大小为 n 的排列 a,使 \sum\limits_{i=2}^n \operatorname{dis}(a_{i-1},a_i)
i=2

n

dis(a
i−1

,a
i

) 最大,求这个最大值
\operatorname{dis}(u,v)dis(u,v) 表示从 u 到 v 的路径的中最短的边的边权,若有多条路径,则选令 \operatorname{dis}(u,v)dis(u,v) 最大的路径
输入描述:
第一行两个正整数 n,m
接下来 m 行,每一行三个正整数 u,v,w 表示 u,v 之间有一条长度为 w 的边
输出描述:
仅一行,表示最大的 \sum\limits_{i=2}^n \operatorname{dis}(a_{i-1},a_i)
i=2

n

dis(a
i−1

,a
i

)
示例1
输入
复制
2 1
1 2 3
输出
复制
3
说明
很显然,1,2 或者 2,1 都是合法的
备注:
对于 100%100% 的数据,1 \leq n,m \leq 5 \times 10^51≤n,m≤5×10
5
,1\leq u,v\leq n1≤u,v≤n,1 \leq w \leq 10^91≤w≤10
9
,保证图联通

一堆废话,其实就是求的是最大生成树

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
#include <ext/rope>
//#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(int out = 0)
{
    char c;
    while((c=getchar()) < 48 || c > 57);
    while(c >= 48 && c <= 57) out=out*10+c-48,c=getchar();
    return out; 
}

const int N = 5e5 + 10;
const int M = N * 10;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

struct Edge{
	int a, b;
	int w;
	bool operator<(const Edge&W)const{
		return w > W.w;
	}
}edge[N];
int n, m;
int p[N];

int find(int x){
	if (x != p[x])   p[x] = find(p[x]);
	return p[x];
}

signed main(){
	scanf("%lld%lld", &n, &m);
	
	for(int i = 1; i <= n; i ++)   p[i] = i;
	
	for (int i = 1; i <= m; i ++){
		int a, b, w;
		scanf("%lld%lld%lld", &a, &b, &w);
		edge[i] = {a, b, w};
	}
		
	sort(edge + 1, edge + 1 + m);
	
	int res = 0;
	
	for (int i = 1; i <= m; i ++){
		int a = edge[i].a, b = edge[i].b, w = edge[i].w;
		int pa = find(a), pb = find(b);
		
		if (pa != pb){
			res += w;
			p[pa] = pb;
		}
	}	
	
	cout << res << endl;
		
	return 0;
}

火柴排队
链接:https://ac.nowcoder.com/acm/contest/7329/D
来源:牛客网

题目描述
金发少女 DK 出了一套比赛,众所周知 DK 并不是一个好出题人,这回他的数据造的太烂了,有一道字符串题被“先搜长串再搜短串”的奇怪算法草了过去,导致有可能有一部分选手的实际分数比他的估分高了 d 分

DK 惊奇地发现,每个人的实际排名和他估分的排名完全一致,他觉得这种事情简直太少见了。假设从 n 位选手中选 k 位增加 d 分的 n\choose k(
k
n

) 种方案的概率均相等,DK 希望你告诉他每个选手排名不变的概率。答案模 998244353

形式化地说:给出 n 个正整数 a_ia
i

,即每个选手的估分和 d,随机使 k 个元素增加 d(n\choose k(
k
n

) 种可能发生的概率相等),求增加后的序列 a’_ia
i


满足如果 a_i<a_ja
i

<a
j

那么 a’_i\le a’_ja
i


≤a
j


的概率

对于每个 1\le k\le n1≤k≤n ,你都要输出其对应的答案。答案模 998244353

具体来说,所求的概率应该是一个有理数 \dfrac a b
b
a

,你要输出的是满足 bx\equiv a\pmod {998244353}bx≡a(mod998244353) 的 x。保证这个方程有解
输入描述:
一行两个整数 n,d

接下来一行 n 个整数 a_ia
i

输出描述:
输出 n 行每行一个整数,第 i 行的整数表示 k=i 时的答案
示例1
输入
复制
5 2
3 4 7 9 8
输出
复制
199648871
898419918
898419918
199648871
1
说明
当 k=1 时,共有5种可能
其中,给 a_2a
2

或 a_4a
4

加 d,满足条件
故概率为 \frac{2}{5}
5
2

,在 mod 998244353 意义下为 199648871
备注:
对于所有数据保证:n\le 5000,a_i\le 10^9 ,d\le 10^9n≤5000,a
i

≤10
9
,d≤10
9
,数据保证 a_ia
i

各不相同

一开始的暴力超时垃圾代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
#include <ext/rope>
//#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(int out = 0)
{
    char c;
    while((c=getchar()) < 48 || c > 57);
    while(c >= 48 && c <= 57) out=out*10+c-48,c=getchar();
    return out; 
}

const int N = 5010;
const int M = N * 10;
const int mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

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

int ni(int x, int y){
	return x % mod * qmi(y, mod - 2) % mod;
}

int n, d;
int a[5010];
int st[5010];
int fact[N], infact[N];
int last[N];
int counts[N];

void init(){
	 fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i ++ )
    {
        fact[i] = fact[i - 1] * i % mod;
        infact[i] = infact[i - 1] * qmi(i, mod - 2) % mod;
    }
}

int C(int a, int b){
	return fact[a] * infact[b] % mod * infact[a - b] % mod;
}

signed main(){
	//cout << ni(3, 10) << endl;
	ios;
	init();
	cin >> n >> d;
	for (int i = 1; i <= n; i ++){
		cin >> a[i];
	}
	
	sort(a + 1, a + 1 + n);
	
	int cnt = 1;
	int last1 = 0;
	for (int i = 1; i <= n; i ++){
		if (a[i] + d < a[i + 1]){
			st[i] = i;
			last[i] = last1;
			counts[i] = i - last1;
			cnt ++;
			last1 = i;
		}
	}
	
	st[n] = n;
	last[n] = last1;
	counts[n] = n - last1;
//	cout << cnt << endl;
	
	for (int k = 1; k <= n; k ++){
		if (k == n){
			cout << "1" << endl;
			continue;
		}
		int a = 0, b = 0;
		a = C(n, k);
		
		bool flag = false;
		for (int i = 1; i <= n; i ++){
			if (st[i]){
				if (counts[i] >= k)   b ++;
				if (flag && k >= 2){
					int temp = i;
					while(last[temp]){
						
						for (int l = 1; l <= counts[temp]; l ++){
							int flag1 = false;
							for (int r = 1; r <= counts[last[temp]]; r ++){
								if (l + r == k){
								//	cout << l << "--" << r << endl;
									b ++;
									flag1 = true;
									break;
								}
							}
						//	if (flag1)   break;
						}
						
						temp = last[temp];
					//	last[temp] = last[temp];
					}
				}
				
				flag = true;
			}
		}
		
		//cout << b << "---" << a << endl;
		cout << ni(b, a) << endl;
	}
	
	return 0;
}

但是很明显的话,答案相互之间有联系关系,发现性质,当两个数的差小于d的话,如果前一个加的话,后面那个一定也得加,然后状态转移即可,具体的在注释

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
#include <ext/rope>
//#include <bits/stdc++.h> 

using namespace std;
using namespace __gnu_cxx;

#define gt(x) x = read()
//#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(int out = 0)
{
    char c;
    while((c=getchar()) < 48 || c > 57);
    while(c >= 48 && c <= 57) out=out*10+c-48,c=getchar();
    return out; 
}

const int N = 5010;
const int M = N * 10;
const long long mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
//const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

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

int ni(int x, int y){
	return x % mod * qmi(y, mod - 2) % mod;
}//分数的求逆元

int n, d;
int a[5010];
long long fact[N], infact[N];
int f[N][N][2]; //表示前i个数选了j个数当前这个数选或者不选的两种情况

void init(){
	 fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i ++ )
    {
        fact[i] = fact[i - 1] * i % mod;
        infact[i] = infact[i - 1] * qmi(i, mod - 2) % mod;
    }
}

long long C(int a, int b){
	return fact[a] % mod * infact[b] % mod * infact[a - b] % mod;
}

signed main(){
	scanf("%d%d", &n, &d);
	for (int i = 1; i <= n; i ++)   scanf("%d", &a[i]);
	
	init();
	f[1][1][1] = 1;
	//f[1][0][0] = 1;
	for (int i = 1; i <= n; i ++)   f[i][0][0] = 1; //已知的所有都初始化
	
	sort(a + 1, a + 1 + n);
	
	for (int i = 2; i <= n; i ++){
		for (int k = 0; k <= n; k ++){
			bool flag;//如果两个数的差小于k的话,前一个加了,后一个一定得加,否则的话随意
			if (a[i] - a[i - 1] < d)   flag = true;
			else   flag = false;
			
			if (flag){
				f[i][k][0] = f[i - 1][k][0];
				f[i][k][1] = f[i - 1][k - 1][1] + f[i - 1][k - 1][0];//前一个数选不选当前这个数都能选
			}
			else{
				f[i][k][0] = f[i - 1][k][0] + f[i - 1][k][1];
				f[i][k][1] = f[i - 1][k - 1][1] + f[i - 1][k - 1][0];
			}
			
			f[i][k][1] %= mod;
			f[i][k][0] %= mod;
		}
	}
	
	for (int i = 1; i <= n; i ++){
		long long ans = f[n][i][0] + f[n][i][1];
		ans %= mod;
	//	cout << ans << "-----" << C(n, i) << endl;
		cout << ni(ans, C(n, i) % mod) % mod << endl;
	}
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值