sc2017新高二&高一模拟赛2 总结

xiaoxiao2021-02-28  118

a 大杂烩

题目描述

石头、剪刀和布是好朋友。 有一天,他们看到电视上播着一个新游戏:石头剪刀布。他们感到非常不高兴,于是他们决定研究该游戏的进阶版:超级英雄大乱斗。 该游戏的玩法和标准的石头剪刀布非常类似,不过有一点不同,这个游戏不是简单的石头、剪刀和布,而是由十个不同的超级英雄进行对打!下面是十个超级英雄分别对打的结果(如果a行b列的值是1的话,表示a能战胜b;如果a行b列的值是-1的话,表示b能战胜a;如果是0,表示平局):

接下来,石头和剪刀玩了很多把游戏,我们知道了他们各把游戏出的超级英雄牌,我们需要计算出石头赢了多少把,剪刀赢了多少把。

对于10%的数据,N=1。 对于50%的数据,石头只会打出SPIDER牌。 对于100%的数据,N<=100000。


输入格式

第一行一个正整数N,表示玩的局数。 接下来N行,每行两个字符串,分别表示石头和剪刀在这一局中出的超级英雄牌。


输出格式

一行两个整数,表示石头和剪刀分别赢的把数。


输入样例

1 SPIDER SPIDER


输出样例

0 0


解题思路(模拟+打表)

大水题。不要打错表,不要打错超级英雄的名字。


代码

#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <algorithm> #include <map> using namespace std; map <string, int> M; int compare[15][15] = { {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {-1, 0, 1, -1, 1, -1, 1, -1, 1, -1}, {-1, -1, 0, 1, 1, -1, 1, 1, -1, 1}, {-1, 1, -1, 0, 1, -1, 1, -1, 1, -1}, {-1, -1, -1, -1, 0, 1, 1, 1, 1, -1}, {-1, 1, 1, 1, -1, 0, 1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, 0, 1, 1, 1}, {-1, 1, -1, 1, -1, 1, -1, 0, 1, -1}, {-1, -1, 1, -1, -1, 1, -1, -1, 0, 1}, {-1, 1, -1, 1, 1, 1, -1, 1, -1, 0} }; int n, cnt1, cnt2; string A, B; int main(){ M["SPIDER"] = 0; M["HULK"] = 1; M["CA"] = 2; M["IRON"] = 3; M["THOR"] = 4; M["DD"] = 5; M["DOOM"] = 6; M["KP"] = 7; M["RS"] = 8; M["HYDRA"] = 9; cin >> n; for(int i = 1; i <= n; i++){ cin >> A >> B; if(compare[M[A]][M[B]] == 1) cnt1 ++; else if(compare[M[A]][M[B]] == -1) cnt2 ++; } printf("%d %d\n", cnt1, cnt2); return 0; }

b 瓜分领土

题目描述

石头、剪刀和布闹别扭了,他们要分家。 他们生活在一个离散的一维空间里,简单点说,他们拥有在一条直线上的N间房子,每间房子有一个风水值(有正有负)。 然后,他们决定将这N间房子分成非空的三个连续段,从左到右数,第一段的房子全部属于石头,第二段的房子全部属于剪刀,第三段的房子全部属于布。 由于他们希望公平,并且又由于剪刀是他们的老大哥,他们决定根据这些条件制定了一个评判标准: 设石头拥有的房子的风水值和为a,剪刀拥有的房子的风水值和为b,布拥有的房子的风水值和为c,剪刀拥有n间房子。 那么通过给定一个参数x。 那么,这种分配的合理值就是max(a,b,c)-min(a,b,c)+x*n. 合理值越小,表示这种分配越合理。 因此,我们现在就是要求出这个最小的合理值。

对于30%的数据,N<=10. 对于70%的数据,N<=1000. 对于100%的数据,N<=100000,保证所有运算结果在long long范围内。


输入格式

第一行一个正整数N。 第二行有N个整数,表示房子的风水值,按从左到右的顺序给出。 第三行一个整数x。


输出格式

一行一个整数,表示最小的合理值。


输入样例

4 1 1 1 1 -1


输出样例

-1


解题思路(枚举+线段树+离散化+二分+数学分类)

本题线段树好题。

暴力枚举断点 O(n2) 有70分,然而我没用long long炸成60。正解就是只枚举一重,设剪刀拥有的最后一间房子的位置为 i ,然后前面石头的最后一间房子j就用线段树找。

至于具体如何做就要分成6种不同情况,消去min和max(解不等式,具体见代码),变成一个和变量 ij (包括 sum[i],sum[j] )有关的函数,同时确定 sum[j] 的范围(连续一段)。通过枚举固定 i ,离散化sum[j],用线段树维护最小的与j有关的那部分,用公式计算,然后就可以 O(nlogn) 搞定了。

考试时我想到过只枚举 i ,然后解不等式分成6种情况讨论。但是我没有立刻去解上下界,导致我没有想到线段树,我想的是二分。然而前面的sum[j]都是离散的一些点,不连续,是没有单调性的,所以我就弃了交暴力。但离散化将离散的点变成连续的一段,就可以作为下标用线段树等一维数据结构维护。这个常见的方法我以后一定要牢记。

为什么还要二分呢?假如我们预处理离散化 sum 数组,后面解出的上下界中可能在原数组中没有,这时就要二分 L R在线段树底层的位置,就是找后继与前趋。倘若不二分也可以,搞出所有上下界后再离散化或者在线段树里加哨兵也行。另外一些细节,比如解出上下界要向哪边取整,线段树中开一个大小为6的数组维护不同值(每种情况算式不同)或每做一次清空一次之类的实现方法,就具体见代码吧。

ps:这题我调试了一个晚上,反复对照数学公式,反复检查线段树,最后发现是二分写错了!找后继的代码复制了找前趋的,然后修改时傻了,将找后继一开始的 L=n,R=0 ,搞得我一晚上连爆了3次零。。


代码

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> #define N 100010 #define oo 1e18 using namespace std; typedef long long LL; int n; LL x, a[N], sum[N], f[N], ans; struct Ab{int id; LL val;} P[N]; int rank[N], H; struct Tnode{ LL v[6]; }tree[N<<2]; bool cmp(Ab A, Ab B){return A.val < B.val;} void build(int root, int L, int R){ if(L == R){ for(int i = 0; i < 6; i++) tree[root].v[i] = oo; return; } int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1; build(Lson, L, mid); build(Rson, mid+1, R); for(int i = 0; i < 6; i++) tree[root].v[i] = oo; } void update(int root, int L, int R, int x, int op, LL val){ if(x > R || x < L) return; if(L == x && x == R){ tree[root].v[op] = (tree[root].v[op] > val) ? val : tree[root].v[op]; return; } int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1; update(Lson, L, mid, x, op, val); update(Rson, mid+1, R, x, op, val); tree[root].v[op] = (tree[Lson].v[op] < tree[Rson].v[op]) ? tree[Lson].v[op] : tree[Rson].v[op]; } LL query(int root, int L, int R, int x, int y, int op){ if(x > R || y < L) return oo; if(x <= L && y >= R) return tree[root].v[op]; int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1; LL temp1 = query(Lson, L, mid, x, y, op); LL temp2 = query(Rson, mid+1, R, x, y, op); return (temp1 < temp2) ? temp1 : temp2; } int Find_p(LL x){ int L = 1, R = n + 1; while(L + 1 < R){ int mid = (L + R) >> 1; if(P[mid].val <= x) L = mid; else R = mid; } return rank[P[L].id]; } int Find_s(LL x){ int L = 0, R = n; while(L + 1 < R){ int mid = (L + R) >> 1; if(P[mid].val >= x) R = mid; else L = mid; } return rank[P[R].id]; } void Work0(int i){ if(i != 1){ LL L = 2 * sum[i] - sum[n], R = sum[i] / 2; if(L > R || R < P[1].val || L > P[n].val) return; L = Find_s(L); R = Find_p(R); f[i] = min(f[i], sum[n] - sum[i] + x * (LL)i + query(1, 1, H, L, R, 0)); } update(1, 1, H, rank[i], 0, - sum[i] - x * (LL)i); } void Work1(int i){ if(i != 1){ LL L = (sum[i] + 1) / 2, R = sum[n] - sum[i]; if(L > R || R < P[1].val || L > P[n].val) return; L = Find_s(L); R = Find_p(R); f[i] = min(f[i], sum[n] - 2 * sum[i] + x * (LL)i + query(1, 1, H, L, R, 1)); } update(1, 1, H, rank[i], 1, sum[i] - x * (LL)i); } void Work2(int i){ if(i != 1){ LL L = (sum[i] + 1) / 2, R = 2 * sum[i] - sum[n]; if(L > R || R < P[1].val || L > P[n].val) return; L = Find_s(L); R = Find_p(R); f[i] = min(f[i], sum[i] - sum[n] + x * (LL)i + query(1, 1, H, L, R, 2)); } update(1, 1, H, rank[i], 2, sum[i] - x * (LL)i); } void Work3(int i){ if(i != 1){ LL L = P[1].val, R = min(2 * sum[i] - sum[n], sum[n] - sum[i]); if(L > R || R < P[1].val || L > P[n].val) return; L = Find_s(L); R = Find_p(R); f[i] = min(f[i], sum[i] + x * (LL)i + query(1, 1, H, L, R, 3)); } update(1, 1, H, rank[i], 3, - 2 * sum[i] - x * (LL)i); } void Work4(int i){ if(i != 1){ LL L = sum[n] - sum[i], R = sum[i] / 2; if(L > R || R < P[1].val || L > P[n].val) return; L = Find_s(L); R = Find_p(R); f[i] = min(f[i], 2 * sum[i] - sum[n] + x * (LL)i + query(1, 1, H, L, R, 4)); } update(1, 1, H, rank[i], 4, - sum[i] - x * (LL)i); } void Work5(int i){ if(i != 1){ LL L = max(2 * sum[i] - sum[n], sum[n] - sum[i]), R = P[n].val; if(L > R || R < P[1].val || L > P[n].val) return; L = Find_s(L); R = Find_p(R); f[i] = min(f[i], - sum[i] + x * (LL)i + query(1, 1, H, L, R, 5)); } update(1, 1, H, rank[i], 5, 2 * sum[i] - x * (LL)i); } int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); sum[0] = 0; for(int i = 1; i <= n; i++) sum[i] = sum[i-1] + a[i]; for(int i = 1; i <= n; i++){ P[i].id = i; P[i].val = sum[i]; } sort(P+1, P+n+1, cmp); rank[P[1].id] = 1; for(int i = 2; i <= n; i++){ rank[P[i].id] = rank[P[i-1].id]; if(P[i].val != P[i-1].val) rank[P[i].id] ++; } H = rank[P[n].id]; build(1, 1, H); for(int i = 2; i < n; i++) f[i] = oo; scanf("%lld", &x); for(int i = 1; i < n; i++){ Work0(i); Work1(i); Work2(i); Work3(i); Work4(i); Work5(i); } ans = oo; for(int i = 2; i < n; i++) ans = min(ans, f[i]); printf("%lld\n", ans); return 0; }

c 进击的石头

题目描述

石头一不小心掉进了一个复杂的地图里! 这个地图是一个由N个节点、M条边组成的连通无向图,可能会有重边,但没有自环,节点编号从1到N。石头一开始在1号节点。由于石头非常害怕,于是开始等概率地随机游走(举个例子,比如说如果当前节点有K条边,那么以1/K的概率走其中一条边),并走了很久很久很久。 突然,石头停下来了,想要知道现在自己在哪个节点。 于是,石头希望你告诉他,他在各个节点的概率。如果不确定的话,输出“-1”。

对于30%的数据,N<=10,M<=20. 对于90%的数据,N<=300,M<=10000. 对于100%的数据,N<=2000,M<=50000.


输入格式

第一行两个正整数N,M。 接下来M行每行两个正整数x,y.表示存在一条(x,y)的无向边。


输出格式

一行N个整数,表示停在各个节点的概率,保留六位小数。如果不确定,只输出一行一个-1.


输入样例

3 3 1 2 2 3 3 1

2 1 1 2


输出样例

0.333333 0.333333 0.333333

-1


解题思路(求概率+判断二分图)

这题我的初次得分是70分,原因是因为不会判断输出-1的情况。

这题有两种做法,先说一说比较暴力的那种。那就是建好图,然后按题目描述的去做,每走完一步就可以得到当前点的概率,然后不断迭代,直到上一次这个点的概率与这次十分接近为止。就是说,频率稳定在一个数附近,我们就认为这个数是概率。当做了足够多的次数频率扔无法稳定时,就代表概率确定,输出-1。这样做能够拿90分。另外一个点会超时。我们设置上一次此点概率 f 与本次此点概率g,满足 |fg|<Eps 时就认为稳定。当所有点都稳定时就退出迭代。所以 Eps 要取一个适当的值。一般8位~9位比较恰当。

然而我考试的时候并没有去想这种类似于“实验求概率”的方法,我利用了一条数学学方程 E(xi)=E(xi) 。学过数学的都知道,这是期望的线性性质。然而类比期望,其实单求概率也是有同样的性质。我们将题目改一下:每个点有一个分数,分数都为1,求石头最后在每个点的期望得分。这样,由于权值是1,期望与概率在数值上就相等了!于是我们便可以用这条方程解题。

这样做有什么好处呢?我们注意到方程左边求得是和的期望,右边是期望的和。左边考虑的是整体,右边对每个变量独立考虑并直接线性相加。在此题中,我们只考虑每个点独立的概率,由于走了无穷多步,所以开始从哪里出发并不重要了,按理说从哪出发最后概率都是一样的。于是这就相当于上一个点可以在任意位置,到达这个点只能通过这个点连出去的边到达。而每个点都有这样连出去的边。于是到达每个点的概率就等于 。于是我们直接统计度数就可以求到所有点的概率了。

这样做是70分的,因为如何判断-1呢?像上面说的,如果对于从每个点出发概率不一样,那么就没有确定的概率。还有一种角度,我们模拟时类似于分层图上的遍历,如果从奇数层出发和从偶数层出发,概率不一样的话,就没有确定的概率。实在不好理解可以从样例入手。我们从1出发,到达2,再不断下去,和从2出发下去的“最终”情形是不同的!

在实验了多组数据后,我们发现一个结论,从1开始染色一种确定的颜色,有两种颜色,每相邻两层换色。如果最后每个点只能染一种颜色,则没有确定的概率!于是我们就是在找二分图!二分图的最小着色数是2,所以1确定了染色,整幅图的颜色就唯一确定了。此时就是-1的情况。

这是为什么呢?lzh大佬没有留下明确的结论。因为这有点涉及数学了。其实我们可以这样理解,如果最后停在任意的点,在此之前走了奇数步和走了偶数步的概率不同,那就没有概率了(频率永远无法稳定)!因为我们不知道停下的时候,走了奇数步或偶数步,我们不知道最后停在了二分图的左边那层还是右边那层。这样从不同点出发,概率也就不同了。数学中,无穷大是没有奇偶性的。于是我们总算勉强说服了自己去求二分图了,二分图的求法很简单,直接dfs模拟染色过程就行了。

时间复杂度 O(n)


代码

#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <algorithm> #define N 2010 using namespace std; int n, m; int deg[N]; bool to[N][N], odd[N], even[N]; void dfs(int now){ for(int i = 1; i <= n; i++){ if(!to[now][i]) continue; if(odd[now] && !even[i]){ even[i] = true; dfs(i); } if(even[now] && !odd[i]){ odd[i] = true; dfs(i); } } } int main(){ scanf("%d%d", &n, &m); int a, b; for(int i = 1; i <= m; i++){ scanf("%d%d", &a, &b); to[a][b] = to[b][a] = true; deg[a] ++; deg[b] ++; } odd[1] = true; dfs(1); for(int i = 1; i <= n; i++) if(!odd[i] || !even[i]){ printf("-1\n"); return 0; } int sum = 0; for(int i = 1; i <= n; i++) sum += deg[i]; for(int i = 1; i <= n; i++){ double possibility = (double)deg[i] / (double)sum; printf("%.6lf ", possibility); } printf("\n"); return 0; }

总结

本次考试并不理想,主要是第二题long long没注意,对线段树的应用有些生疏,第三题也没好好去想和分析样例,数学基础不够扎实。

以后要多巩固复习,数据结构和期望、概率等方面一定要加强弥补。


转载请注明原文地址: https://www.6miu.com/read-46613.html

最新回复(0)