时间复杂度
链接: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;
}