Contest Info


[Practice Link](https://cn.vjudge.net/contest/313014)

Solved A B C D E F G H I J K
9/11 O - Ø O - O O O O O O
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A. Cotree

题意:

有两棵树,一共有\(n\)个点,现在要求在两棵树中连一条边,使得他们变成一棵树,如何连边使下式最小:

\[\begin{eqnarray*}
\sum\limits_{i = 1}^n \sum\limits_{j = i + 1}^n dis(i, j)
\end{eqnarray*}
\]

思路:

我们可以单独考虑边的贡献,一条边的贡献是这条边两边的点数乘积。

那么新加的边的贡献是固定的。

我们考虑怎么减少已存在的边的贡献,其实我们可以感性的理解一下,我们最后选出的两个点相连,我们强制让它们成为他们各自树中的根,那么这么考虑:

  • 令\(f[i]\)表示以\(i\)为根的子树中的边的贡献

    那么转移有:

\[\begin{eqnarray*}
f[u] = \sum\limits_{v \in son[u]} f[v] + sze[v] * (n - sze[v])
\end{eqnarray*}
\]

  • 令\(g[i]\)表示以\(i\)为根的非子树中的边的贡献

    那么转移有:

\[\begin{eqnarray*}
g[u] = g[fa[u]] + f[fa[u]] - f[u] - (sze[u]) * (n - sze[u]) + (sze[u] + S) * (n - sze[u] - S)
\end{eqnarray*}
\]

其中\(S\)代表另外一棵树的大小,最后那两部分主要是\(u \rightarrow fa[u]\)那条边的贡献变了

代码:

#include <bits/stdc++.h>
using namespace std; #define ll long long
#define N 100010
int n, S, T;
vector <vector<int>> G; int fa[N], sze[N];
ll f[N], g[N];
void DFS(int u) {
sze[u] = 1;
f[u] = 0;
for (auto v : G[u]) if (v != fa[u]) {
fa[v] = u;
DFS(v);
sze[u] += sze[v];
f[u] += f[v];
f[u] += 1ll * sze[v] * (n - sze[v]);
}
} ll DFS2(int u, int rt, int S) {
if (u != rt) {
g[u] = g[fa[u]] + f[fa[u]] - f[u] + 1ll * (n - sze[u] - S) * (sze[u] + S) - 1ll * sze[u] * (n - sze[u]);
} else {
g[u] = 0;
}
ll res = f[u] + g[u];
for (auto v : G[u]) if (v != fa[u]) {
res = min(res, DFS2(v, rt, S));
}
return res;
} int main() {
while (scanf("%d", &n) != EOF) {
G.clear(); G.resize(n + 1);
for (int i = 1; i <= n; ++i) fa[i] = -1;
for (int i = 1, u, v; i < n - 1; ++i) {
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
int rt[2]; rt[0] = 1;
fa[1] = 1;
DFS(1); S = sze[1]; T = n - S;
for (int i = 1; i <= n; ++i) {
if (fa[i] == -1) {
rt[1] = i;
fa[i] = i;
DFS(i);
break;
}
}
ll res = DFS2(1, 1, T) + DFS2(rt[1], rt[1], S) + 1ll * S * T;
printf("%lld\n", res);
}
return 0;
}

C.Trap

题意:

有\(n\)根长度为\(a_i\)的棍子,询问有多少种方案选出四条边,使得它们的\(gcd = 1\),并且能够组成一个等腰梯形。

思路:

考虑\(f(d)\)为长度为\(d\)的倍数的棍子组成的方案数,那么可以容斥原理有:

\[\begin{eqnarray*}
ans = \sum\limits_{i = 1}^{maxlen} f(i)\mu(i)
\end{eqnarray*}
\]

其中\(\mu(i)\)为莫比乌斯函数,它的定义如下:

\[\begin{eqnarray*}
\mu(n) =
\left\{
\begin{array}{cccc}
1 && n = 1 \\
(-1)^k && n = p_1p_2, \cdots p_k \text{n中无平方因数} \\
0 && \text{n中有>1的平方因数}
\end{array}
\right.
\end{eqnarray*}
\]

根据\(\mu(n)\)的定义,我们发现当\(n\)中奇数个质因子的时候\(\mu(n) = -1\),当\(n\)中有偶数个质因子的时候\(\mu(n) = 1\),恰好是我们需要的容斥系数。

考虑怎么求解\(f(d)\),可以两个循环枚举上底和腰:

  • 下底的范围是(上底, 上底 + 腰 * 2)
  • 注意特判下底等于腰的情况和上底等于腰的情况,棍子是否足够

为什么下底的下界是上底 + 腰 * 2 ?

考虑等腰的那个等角越小的时候,下底越长,那么极限情况,就是角度为\(0\)的时候,这时候长度就是上底 + 腰 * 2, 但是这个长度不合法,所以是开区间。

代码:

#include <bits/stdc++.h>
using namespace std; #define ll long long
#define M 10010
int n, a[M], cnt[M];
vector <vector<int>> vec, fac;
int prime[M], check[M], mu[M], tot; void init() {
tot = 0;
memset(check, 0, sizeof check);
mu[1] = 1;
fac.clear();
fac.resize(M);
for (int i = 2; i < M; ++i) {
if (!check[i]) {
prime[++tot] = i;
mu[i] = -1;
}
for (int j = 1; j <= tot; ++j) {
if (1ll * i * prime[j] >= M) break;
check[i * prime[j]] = 1;
if (i % prime[j] == 0) {
mu[i * prime[j]] = 0;
break;
} else {
mu[i * prime[j]] = -mu[i];
}
}
}
for (int i = 1; i < M; ++i) {
for (int j = i; j < M; j += i) {
fac[j].push_back(i);
}
}
} int main() {
init();
while (scanf("%d", &n) != EOF) {
vec.clear(); vec.resize(M);
memset(cnt, 0, sizeof cnt);
for (int i = 1; i <= n; ++i) {
scanf("%d", a + i);
++cnt[a[i]];
for (auto it : fac[a[i]]) {
vec[it].push_back(a[i]);
}
}
ll res = 0;
for (int i = 1; i < M; ++i) {
if (vec[i].empty() || mu[i] == 0) continue;
ll tmp = 0;
sort(vec[i].begin(), vec[i].end());
vec[i].erase(unique(vec[i].begin(), vec[i].end()), vec[i].end());
int sze = vec[i].size();
for (auto it : vec[i]) { //枚举上边
int l = 0, r = 0;
while (l < sze && vec[i][l] < it + 1) ++l;
for (auto it2 : vec[i]) { //枚举腰
if (it != it2 && cnt[it2] >= 2) {
while (r < sze - 1 && vec[i][r + 1] < it + 2 * it2) ++r;
if (l < sze && r < sze && l <= r) {
tmp += r - l + 1;
if (vec[i][l] <= it2 && it2 <= vec[i][r]) {
if (cnt[it2] < 3) --tmp;
}
}
} else if (it == it2 && cnt[it] >= 3) {
while (r < sze - 1 && vec[i][r + 1] < it + 2 * it2) ++r;
if (l < sze && r < sze && l <= r) {
tmp += r - l + 1;
}
}
}
}
res += tmp * mu[i];
}
printf("%lld\n", res);
}
return 0;
}

D.Wave

题意:

找一个最长的'wave',要求满足以下限制:

  • 序列长度大于等于\(2\)
  • 所有奇数位置上的数相同
  • 所有偶数位置上的数相同
  • 奇数位置和偶数位置的数不同

思路:

因为值域只有\([1, 100]\),那么暴力枚举奇数位置上的数和偶数位置上的数即可。

时间复杂度\(\mathcal{O}(nc)\)

代码:

#include <bits/stdc++.h>
using namespace std; #define N 100010
int n, c, a[N];
int f[N][110], nx[110]; int main() {
scanf("%d%d", &n, &c);
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
for (int i = 1; i <= c; ++i) nx[i] = n + 1, f[n + 1][i] = n + 1;
for (int i = n; i >= 0; --i) {
for (int j = 1; j <= c; ++j) {
f[i][j] = nx[j];
}
if (i) nx[a[i]] = i;
}
int res = 0;
for (int i = 1; i <= c; ++i) {
for (int j = 1; j <= c; ++j) if (i != j) {
int tmp = 0;
int it = 0;
while (it <= n) {
it = f[it][i];
if (it <= n) {
tmp += 1;
} else break;
it = f[it][j];
if (it <= n) {
tmp += 1;
} else break;
}
if (tmp > 1) {
res = max(res, tmp);
}
}
}
printf("%d\n", res);
return 0;
}

F.String

题意:

给出一个字符串,只包含'a', 'v', 'i', 'n', 要求从中等概率可重复的取出四个字符,问恰好是'avin'的概率是多少。

思路:

根据乘法原理计算即可。

代码:

#include <bits/stdc++.h>
using namespace std; #define ll long long
#define N 110
int n;
char s[N];
int cnt[220]; int main() {
while (scanf("%d", &n) != EOF) {
scanf("%s", s + 1);
memset(cnt, 0, sizeof cnt);
for (int i = 1; i <= n; ++i) ++cnt[s[i]];
if (!cnt['a'] || !cnt['v'] || !cnt['i'] || !cnt['n']) {
puts("0/1");
} else {
ll a = 1ll * cnt['a'] * cnt['v'] * cnt['i'] * cnt['n'];
ll b = 1ll * n * n * n * n;
ll G = __gcd(a, b);
a /= G;
b /= G;
printf("%lld/%lld\n", a, b);
}
}
return 0;
}

G. Traffic

题意:

在一个十字路口,有\(n\)辆东西走向的车,他们会在\(a_i\)时刻到达,有\(m\)辆南北走向的车,他们会在\(b_i\)时刻到达。问需要让\(m\)辆南北走向的车整体等待多少秒,使得他们的开始行动之后不会和东西走向的车相撞?

思路:

考虑\(a_i\)和\(b_i\)只有\(1000\),那么等待时间不会超过\(1000\),暴力枚举即可。

代码:

#include <bits/stdc++.h>
using namespace std; #define N 5100
int n, m, a[N], b[N]; bool ok(int x) {
for (int i = 1; i <= m; ++i) {
if (a[b[i] + x]) {
return 0;
}
}
return 1;
} int main() {
while (scanf("%d%d", &n, &m) != EOF) {
memset(a, 0, sizeof a);
for (int i = 1, x; i <= n; ++i) {
scanf("%d", &x);
a[x] = 1;
}
for (int i = 1; i <= m; ++i) scanf("%d", b + i);
for (int i = 0; i <= 2000; ++i) {
if (ok(i)) {
printf("%d\n", i);
break;
}
}
}
return 0;
}

H.Rng

题意:

考虑随机选择一个区间的过程:

  • 先从\([1, n]\)等概率选择\(r\)
  • 再在\([1, r]\)中等概率选择出\(l\)

    问随机选择两个区间,它们相交的概率?

思路:

考虑枚举\(r\):

  • 考虑\(R\)落在\([r + 1, n]\)的概率,即为:

\[\begin{eqnarray*}
\frac{1}{n^2} \sum\limits_{i = r + 1}^n \frac{r}{i}
\end{eqnarray*}
\]

  • 考虑\(R\)落在\([l, r]\)之间的概率,即为:

\[\begin{eqnarray*}
\frac{1}{n}(\frac{1}{r} \sum\limits_{i = 1}^r \frac{r - i +1}{n})
\end{eqnarray*}
\]

代码:

#include <bits/stdc++.h>
using namespace std; #define ll long long
#define N 1000010
const ll p = 1e9 + 7;
ll qmod(ll base, ll n) {
ll res = 1;
while (n) {
if (n & 1) {
res = res * base % p;
}
base = base * base % p;
n >>= 1;
}
return res;
}
int n;
ll f[N], g[N], inv[N]; ll Sf(int l, int r) {
if (l > r) return 0;
return (f[r] - f[l - 1] + p) % p;
} ll Sg(int l, int r) {
if (l > r) return 0;
return (g[r] - g[l - 1] + p) % p;
} void add(ll &x, ll y) {
x += y;
if (x >= p) x -= p;
} int main() {
inv[1] = 1;
for (int i = 2; i < N; ++i) inv[i] = inv[p % i] * (p - p / i) % p;
for (int i = 1; i < N; ++i) {
f[i] = (f[i - 1] + i) % p;
g[i] = (g[i - 1] + inv[i]) % p;
}
while (scanf("%d", &n) != EOF) {
ll res = 0;
for (int i = 1; i <= n; ++i) {
add(res, 1ll * i * inv[n] % p * inv[n] % p * Sg(i + 1, n) % p);
add(res, 1ll * inv[i] * inv[n] % p * inv[n] % p * (1ll * i * (i + 1) % p - Sf(1, i) + p) % p);
}
printf("%lld\n", res);
}
return 0;
}

I. Budget

签到题。

#include <bits/stdc++.h>
using namespace std; #define ll long long
#define N 1100
int n;
char s[N]; ll get() {
scanf("%s", s + 1);
int len = strlen(s + 1);
int pos = -1;
for (int i = 1; i <= len; ++i) {
if (s[i] == '.') {
pos = i;
break;
}
}
if (pos == -1 || len - pos < 3) return 0;
int num = s[pos + 3] - '0';
if (num <= 4) return -num;
else return 10 - num;
} int main() {
while (scanf("%d", &n) != EOF) {
ll res = 0;
for (int i = 1; i <= n; ++i) {
res += get();
}
printf("%.3f\n", res * 0.001);
}
return 0;
}

J. Worker

题意:

有\(n\)个销售点,有\(m\)个销售员,每个销售员在第\(i\)个销售点会产生\(a_i\)的订单,问如何分配销售员使得每个销售点产生的订单相同。

思路:

首先单个销售点的订单量肯定是所有销售点\(a_i\)的最小公倍数的倍数。

那么判断\(m\)能否整除其最小公倍数即可,然后按比例分配。

代码:

#include <bits/stdc++.h>
using namespace std; #define N 1100
#define ll long long
int n, a[N];
ll m, lcm; void solve() {
ll one = 0;
for (int i = 1; i <= n; ++i) {
one += lcm / a[i];
}
if (m % one) {
puts("No");
return;
}
ll cur = m / one;
puts("Yes");
for (int i = 1; i <= n; ++i) printf("%lld%c", cur * (lcm / a[i]), " \n"[i == n]);
} int main() {
while (scanf("%d%lld", &n, &m) != EOF) {
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
lcm = a[1];
for (int i = 2; i <= n; ++i) {
lcm = lcm * a[i] / __gcd(lcm, 1ll * a[i]);
}
solve();
}
return 0;
}

K. Class

题意:

给出:

\[\begin{cases}
x &=& a + b \\
y &=& a - b
\end{cases}
\]

计算\(a \cdot b\)。

思路:

签到题。

代码:

#include <bits/stdc++.h>
using namespace std; int main() {
int x, y;
while (cin >> x >> y) {
int a = (x + y) / 2;
int b = (x - y) / 2;
cout << a * b << "\n";
}
return 0;
}

2019CCPC-江西省赛的更多相关文章

  1. 2021江西省赛线下赛赛后总结(Crypto)

    2021江西省赛线下赛 crypto1 题目: from random import randint from gmpy2 import * from Crypto.Util.number impor ...

  2. 2021 ICPC 江西省赛总结

      比赛链接:https://ac.nowcoder.com/acm/contest/21592   大三的第一场正式赛,之前的几次网络赛和选拔赛都有雄哥坐镇,所以并没有觉得很慌毕竟校排只取每个学校成 ...

  3. 2019-CCPC广东省赛总结

    2018年11月第一次参加ICPC区域赛青岛赛区,打铁了! 2019年5月第一次参加CCPC广东省赛,4题滚粗,C题莫队TLE13发,只拿了个铜牌! 教训总结: 比赛时千万不能犹豫,不能犹豫,不能犹豫 ...

  4. [2019CCPC网络赛][hdu6704]K-th occurrence(后缀数组&&主席树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6704 题意为查询子串s[l...r]第k次出现的位置. 写完博客后5分钟的更新 写完博客才发现这份代码 ...

  5. I - Union 2019ccpc女生赛

    I - Union 这是2019女生赛最难的一个题目,但是现在去写,我觉得没有想象之中的那么难. 把这个题目分成几个部分来考虑. 假设给你k个数,让你分成三个集合,满足这四个条件,且不需要考虑时间和空 ...

  6. CCPC2019江西省赛-Problem G.Traffic

    题目描述: /*纯手打题面*/ Avin is observing the cars at a crossroads.He finds that there are n cars running in ...

  7. 2019CCPC网络赛 C - K-th occurrence HDU - 6704(后缀数组+ST表+二分+主席树)

    题意 求区间l,r的子串在原串中第k次出现的位置. 链接:https://vjudge.net/contest/322094#problem/C 思路 比赛的时候用后缀自动机写的,TLE到比赛结束. ...

  8. 2019CCPC网络赛

    ^&^ (HDU 6702) Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Other ...

  9. 2019CCPC网络赛 HD6707——杜教筛

    题意 求 $f(n,a,b)=\sum_{i=1}^n \sum_{j=1}^i gcd(i^a-j^a,i^b-j^b)[gcd(i,j)=1]\%(10^9+7)$,$1 \le n,a,b \l ...

  10. 2019CCPC网络赛 HDU 6702——找规律

    题意 给定 $A,B$(都是正整数),求使得 $(A\  xor\  C) \& (B \ xor \  C)$ 最小的正整数 $C$,如果有多个满足条件的 $C$,输出最小的 $C$. 分析 ...

随机推荐

  1. jq获取元素偏移量offset()

    解释: 1 获取匹配元素在当前视口的相对偏移. 2 返回的对象包含两个整型属性:top 和 left demo1: 获取top与left var aaa = $(".aaa "); ...

  2. poj 3069 继续弱鸡的贪心

    题意:给出指路石的范围,问最小需要几个指路石可以覆盖所有的军队. 题解:排序一遍,然后扫出起始区间和终止区间,就可以求出最小的覆盖数了 ac代码: #include <iostream> ...

  3. CCF 201809-1 卖菜

    题目: 问题描述 在一条街上有n个卖菜的商店,按1至n的顺序排成一排,这些商店都卖一种蔬菜. 第一天,每个商店都自己定了一个价格.店主们希望自己的菜价和其他商店的一致,第二天,每一家商店都会根据他自己 ...

  4. c#获取本月有哪些周六、周日

    最近项目中有用到本月所有的周六,周日,特此分享一下! 算法思路:写一个循环,条件为本月开始日期.本月截至日期,通过循环获取第一个周六,加一天就是周日,每增加六天就是下一个周六,依次类推,循环到月末 代 ...

  5. JQuery攻略读书笔记---第2章 数组

      2 数组2.8 创建对象数组循环数组2.9 数组排序 2 数组 2.8 创建对象数组 //数组化对象 var student =[ { "role":101, "na ...

  6. 远程调用cmd更新本地jar

    最近遇到一个项目需求需要实现远程更新,但是本地项目无法更新自己,这让博主很是头疼,既然自己无法更新自己的话,那就自建新的项目,通过本地项目来调用新项目接口来更新本地项目. 代码如下: /** * 重启 ...

  7. MySQL数据库的创建&删除&选择

    1.MySQL数据库的创建 方法1和2      方法3.使用PHP脚本 PHP中使用mysqli_query函数来创建或删除MySql数据库 mysqli_query函数:两个参数 返回值:执行成功 ...

  8. FlowPortal BPM 明细表中新添加的行一直排在最后的问题

    明细表中的数据提交过之后再编辑时,添加的行不管在第几行添加都显示在最后一行的问题 Solution:明细表的数据库表中加字段OrderIndex,设为必填,系统会自动排序

  9. c#NPOI导出2007版本excel

    2003和2007版本区别: HSSFWorkbook(2003) IWorkbook(2007版本) 写完之后会有个问题,导出会报错[流已关闭]. NPOI生产.xlsx文件件时,在使用book.W ...

  10. EF数据Linq方式查询

    using (var ctx = new NorthwindEntities()) { //单表查询SQL查询方式 //SELECT * FROM Customers AS c WHERE c.Cit ...