@wsndy-xx
2019-08-18T11:37:21.000000Z
字数 5183
阅读 940
多做几套模拟题,体会心路历程。
from hzwer 模拟赛整理 2014-10-5 NOIP模拟赛
time 08.17
忘记做过多长时间了,应该不到 3h
已知一棵n个节点的有根树。有m个询问。每个询问给出了一对节点的编号x和y,询问x与y的祖孙关系。
对于30%的数据,n,m≤1000。
对于100%的.据,n,m≤40000,每个节点的编号都不超过40000。
算法分析:存在祖孙关系的话二者的 Lca 一定为其中之一, 求 Lca 判断即可
得分分析:放在一年以前,20min AC 不在话下,现在的码力确实大减啊。
这种难度的题目就是给我这种水平的选手提供的吧。
代码:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <algorithm>#include <cstring>using namespace std;const int N = 5e4 + 10;#define gc getchar()inline int read() {int x = 0, f = 1; char c = gc;while(c < '0' || c > '9') {if(c == '-') f = -1; c = gc;}while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;return x * f;}int head[N], now = 1;int n;struct Node {int v, nxt;} G[N << 1];int fa[N], son[N], size[N], topp[N], deep[N];int root;void Add(int u, int v) {G[now].v = v; G[now].nxt = head[u]; head[u] = now ++;}void Dfs1(int u, int f, int dep) {fa[u] = f, deep[u] = dep; size[u] = 1;for(int i = head[u]; ~ i; i = G[i].nxt) {int v = G[i].v;if(v != f) {Dfs1(v, u, dep + 1);size[u] += size[v];if(size[son[u]] < size[v]) son[u] = v;}}}void Dfs2(int u, int tp) {topp[u] = tp;if(!son[u]) return ;Dfs2(son[u], tp);for(int i = head[u]; ~ i; i = G[i].nxt) {int v = G[i].v;if(v != fa[u] && v != son[u]) {Dfs2(v, v);}}}int Lca(int x, int y) {int tpx = topp[x], tpy = topp[y];while(tpx != tpy) {if(deep[tpx] < deep[tpy]) swap(x, y), swap(tpx, tpy);x = fa[tpx];tpx = topp[x];}if(deep[x] < deep[y]) swap(x, y);return y;}int main() {freopen("tree.in", "r", stdin);freopen("tree.out", "w", stdout);n = read();for(int i = 1; i <= N - 5; i ++) head[i] = -1;for(int i = 1; i <= n; i ++) {int u = read(), v = read();if(v == -1) root = u;else Add(u, v), Add(v, u);}Dfs1(root, 0, 0);Dfs2(root, root);int q = read();for( ; q; q --) {int x = read(), y = read();int lca = Lca(x, y);if(lca == x) {puts("1");} else if(lca == y) {puts("2");} else {puts("0");}}return 0;}
【问题描述】
有两个队伍A和B,每个队伍都有n个人。这两支队伍之间进行n场1对1比赛,每一场都是由A中的一个选手与B中的一个选手对抗。同一个人不会参加多场比赛,每个人的对手都是随机而等概率的。例如A队有A1和A2两个人,B队有B1和B2两个人,那么(A1 vs B1,A2 vs B2)和(A1 vs B2,A2 vs B1)的概率都是均等的50%。
每个选手都有一个非负的实力值。如果实力值为X和Y的选手对抗,那么实力值较强的选手所在的队伍将会获得(X-Y)^2的得分。
求A的得分减B的得分的期望值。
【输入格式】
第一行一个数n表示两队的人数为n。
第二行n个数,第i个数A[i]表示队伍A的第i个人的实力值。
第三行n个数,第i个数B[i]表示队伍B的第i个人的实力值。
【输出格式】
输出仅包含一个实数表示A期望赢B多少分。答案保留到小数点后一位(注意精度)。
【样例输入】
2
3 7
1 5
【样例输出】
20.0
【数据规模】
对于30%的数据,n≤50。
对于100%的.据,n≤50000;A[i],B[i]≤50000
算法分析:根据期望的和=和的期望,只需把A队每个人的期望得分减去B队每个人的期望得分即为答案。
两个人相遇的概率为 (1/n),
假设固定 B 的选手不动,对 A 进行排列会存在 (n!) 种情况,其中 A 的选手 a 与 B 的选手 b 相遇的情况有 (n - 1) ! 种,所以相遇的概率为 (1/n)
对于贡献,排序后可以线性算出,lower_bound 也可以,只不过算价值时复杂度带 log,不过无所谓啦。
得分分析:这道题一开始想的是暴力枚举所有的情况然后计算,不过部分分不允许这样做,后来想到了正解的做法,只不过两者相遇的概率想错了,后来‘偷了’两份大样例然后乱试一番找到了正确的概率,写了出来,不过卡精度这种事虽然经常听说但是我还是第一次遇到。。。
代码:
#include <iostream>#include <string>#include <cstdio>#include <cmath>#include <algorithm>#include <cstdlib>using namespace std;const int N = 5e4 + 10;double a[N], b[N];int n;double f[N], sum[N], sumf[N];int main() {freopen("mat.in", "r", stdin);freopen("mat.out", "w", stdout);scanf("%d", &n);for(int i = 1; i <= n; i ++) scanf("%lf", &a[i]);for(int i = 1; i <= n; i ++) scanf("%lf", &b[i]);sort(a + 1, a + n + 1);sort(b + 1, b + n + 1);for(int i = 1; i <= n; i ++) f[i] = b[i] * b[i];for(int i = 1; i <= n; i ++) sumf[i] = sumf[i - 1] + f[i];for(int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + b[i];double Ansa = 0;for(int i = 1; i <= n; i ++) {int wei = lower_bound(b, b + n + 1, a[i]) - b;wei --;if(wei < 1) continue;double js = (double) wei;Ansa += (js * a[i] * a[i] - 2 * sum[wei] * a[i] + sumf[wei]);}for(int i = 1; i <= n; i ++) f[i] = a[i] * a[i];for(int i = 1; i <= n; i ++) sumf[i] = sumf[i - 1] + f[i];for(int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + a[i];double Ansb = 0;for(int i = 1; i <= n; i ++) {int wei = lower_bound(a, a + n + 1, b[i]) - a;wei --;if(wei < 1) continue;double js = (double) wei;Ansb += (js * b[i] * b[i] - 2 * sum[wei] * b[i] + sumf[wei]);}double nn = n * 1.0;double Answer = (Ansa - Ansb) / nn;printf("%.1lf", Answer);return 0;}
【问题描述】
一个数字被称为好数字当他满足下列条件:
1. 它有2*n个数位,n是正整数(允许有前导0)。
2. 构成它的每个数字都在给定的数字集合S中。
3. 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等
例如对于n=2,S={1,2},合法的好数字有1111,1122,1212,1221,2112,2121,2211,2222这样8种。
已知n,求合法的好数字的个数mod 999983。
【输入格式】
第一行一个数n。
接下来一个长度不超过10的字符串,表示给定的数字集合。
【输出格式】
一行一个数字表示合法的好数字的个数mod 999983。
【样例输入】
2
0987654321
【样例输出】
1240
【数据规模】
对于20%的数据,n≤7。
对于100%的.据,n≤1000,|S|≤10。
算法分析:
ANS=前n位之和与后n位之和相等的方案数+奇数位之和与偶数位之和相等的方案数-前n位之和与后n位之和相等且奇数位之和与偶数位之和相等的方案数
前2个需要+的方案数都很好求,直接递推,重点是最后一个要满足2个条件的方案数怎么求,其实也很简单:
因为前n位之和=后n位之和,奇数位之和=偶数位之和
所以前n位中奇数位之和=后n位中偶数位之和 且
前n位中偶数位之和=后n位中奇数位之和
现在只要求上面这个问题的方案数,由于两个等式中的元素无交集,也是十分好算的。
得分分析:这题不给暴力分我怎么得分呢?
代码:
#include <iostream>#include <algorithm>#include <iomanip>#include <functional>#include <fstream>#include <cstring>#include <cstdio>#include <cstdlib>using namespace std;const int maxn = 1000 + 50;const int maxsum = 10000 + 50;const int mo = 999983;ifstream fin("num.in");ofstream fout("num.out");int f[maxn][maxsum] = {0};int n;bool canuse[10] = {0};int maxvalue = 0;long long ans = 0;void ReadData() {fin >> n;char c;while (fin >> c) {canuse[c - '0'] = true;maxvalue = max(maxvalue, c - '0');}}void DP() {f[0][0] = 1;for (int i = 0; i <= maxvalue; i++) {if (canuse[i]) {f[1][i] = 1;}}for (int i = 1; i < n; i++) {for (int j = 0; j <= maxvalue * i; j++) {for (int k = 0; k <= maxvalue; k++) {if (canuse[k]) {f[i + 1][j + k] += f[i][j];f[i + 1][j + k] %= mo;}}}}}inline long long sqr(long long X) {return X * X;}void CalcAns() {for (int i = 0; i <= maxvalue * n; i++) {ans += 2 * sqr(f[n][i]);ans %= mo;}int a = int(n / 2.0 - 1e-8) + 1;int b = n >> 1;long long A = 0;long long B = 0;for (int i = 0; i <= maxvalue * a; i++) {A += sqr(f[a][i]);A %= mo;}for (int i = 0; i <= maxvalue * b; i++) {B += sqr(f[b][i]);B %= mo;}ans = (ans + mo - A * B % mo) % mo;}int main() {ReadData();DP();CalcAns();fout << ans << endl;return 0;}
总结:个人感觉这套题目的难度比较接近 Noip2018 Day1 (noip2019 没了, upset)。不过对于第三题的评价个人无法完成。
对于 T2 如果有大样例,并且不存在卡精度的数据的话可以在 90min 内解决这道题目,但是这题卡精度能得多少分这就只能看命了。
20min + 90min + 0min = 110min 的时间内得到 100 + 60(应该是100啊) + 0 = 160分 的成绩。水平有限。