@wsndy-xx
2019-08-18T19:37:21.000000Z
字数 5183
阅读 728
多做几套模拟题,体会心路历程。
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分 的成绩。水平有限。