一、20200208 第一场
进度(9/10) 未完成:J
A、honoka和格点三角形
1、链接
https://ac.nowcoder.com/acm/contest/3002/A
2、题面
honoka最近在研究三角形计数问题。
她认为,满足以下三个条件的三角形是“好三角形”。
1.三角形的三个顶点均为格点,即横坐标和纵坐标均为整数。
2.三角形的面积为 。
3.三角形至少有一条边和 轴或 轴平行。
honoka想知道,在平面中选取一个大小为 的矩形格点阵,可以找到多少个不同的“好三角形”?由于答案可能过大,请对 取模。
3、思路
根据题目限制条件,这样的三角形可以分类为:底边水平,底边垂直;每一类底边可以为1也可以为2,所以总共为4类。
以底边垂直且长度为1为例,如图所示,对于n * m的矩形格点阵,存在(n - 1) * m个这样的底边;对于每一个这样的底边,可以找出n个向右延伸的三角形。排除最靠右的两列,总共n * (m - 2) * (n - 1),同理加上向左延伸的三角形,共有2 * (n - 1) * (m - 2) * n个此类型三角形。
同理,底边垂直长度为2的三角形有2 * (n - 2) * (m - 1) * n个;
底边水平长度为1的三角形也有2 * (m - 1) * (n - 2) * m个,但是由于其中的直角三角形在之前已经被计算过了,所以要稍微修改一下:(m - 1) * (n - 2) * (2 * m - 4)。
同理,底边水平长度为2的三角形有(m - 2) * (n - 1) * (2 * m - 4)个。
答案就是把上述四种三角形加起来。
到现在这道题就做完了,但由于n, m <= 10 ^ 9,计算时超出long long范围,直接相加会溢出(我懒得加了然后就WA了),所以要化成一个合适的形式,因式分解一下就行了。
4、代码
1 #include2 using namespace std; 3 4 #define MOD 1000000007 5 typedef long long ll; 6 7 ll n, m; 8 9 int main() { 10 cin >> n >> m; 11 cout << ((2 * m + 2 * n - 4) % MOD) * ((2 * n * m - 3 * n - 3 * m + 4) % MOD) % MOD; 12 return 0; 13 }
B、kotori和bangdream
1、链接
https://ac.nowcoder.com/acm/contest/3002/B
2、题面
有一天,kotori发现了一个和lovelive相似的游戏:bangdream。令她惊讶的是,这个游戏和lovelive居然是同一个公司出的!
kotori经过一段时间的练习后已经变得非常触,每个音符 x% 的概率perfect,获得 分, (100−x)% 概率great,获得 分。
已知一首歌有 个音符。kotori想知道,不考虑连击加成的话,一首歌得分的期望是多少?
3、思路
签到题。思路略。这里学到了一个cout保留小数的方法: cout << fixed << setprecision(x) << ...,x表示保留小数位数。
4、代码
1 #include2 using namespace std; 3 4 double n, x, a, b; 5 6 int main() { 7 cin >> n >> x >> a >> b; 8 cout << fixed << setprecision(2) << n * (x / 100 * a + (1 - x / 100) * b); 9 return 0; 10 }
C、umi和弓道
1、链接
https://ac.nowcoder.com/acm/contest/3002/C
2、题面
umi对弓道非常痴迷。
有一天,她在研究一个射箭问题:
在一个无限大的平面中,她站在 这个坐标。
有 个靶子,第 个靶子的坐标是
umi准备在 轴或 轴上放置一块挡板来挡住弓箭的轨迹,使得她可以射中的靶子数量不超过 个。
她想知道挡板的最短长度是多少?
注:假定弓箭的轨迹是起点为umi坐标、长度无穷大的射线。umi和靶子的体积可以无视。挡板的边缘碰到弓箭轨迹也可视为挡住弓箭。
注2:挡板不能弯折,起始和终点必须在同一坐标轴上。
3、思路
简单计算几何。当且仅当射的靶子和(x0, y0)不在同一象限,才可能被挡住。挡板要么在x轴,要么在y轴,则分类讨论:
① 靶子与(x0, y0)在x轴两侧,则x轴挡板可以挡该靶子;
② 靶子与(x0, y0)在y轴两侧,则y轴挡板可以挡该靶子。
利用计算几何知识,求出两种情况下两点所在直线与x/y轴相交的位置,并进行排序,然后双指针从左至右进行扫描,找到最小值即可。
4、代码
1 #include2 using namespace std; 3 4 #define MAXN 100005 5 #define INF 0x3f3f3f3f 6 7 double a, b, X, Y; 8 int n, k, tx, ty; 9 double xl[MAXN], yl[MAXN], xx[MAXN], xy[MAXN], yx[MAXN], yy[MAXN], ans = INF; 10 11 int main() { 12 cin >> X >> Y >> n >> k; 13 for (int i = 1; i <= n; i++) { 14 cin >> a >> b; 15 if (a * X < 0 || a == 0) yx[++ty] = a, yy[ty] = b; 16 if (b * Y < 0 || b == 0) xx[++tx] = a, xy[tx] = b; 17 } 18 for (int i = 1; i <= tx; i++) 19 xl[i] = (xx[i] * Y - xy[i] * X) / (Y - xy[i]); 20 for (int i = 1; i <= ty; i++) 21 yl[i] = (yy[i] * X - yx[i] * Y) / (X - yx[i]); 22 sort(xl + 1, xl + tx + 1), sort(yl + 1, yl + ty + 1); 23 for (int i = 1; i <= tx - n + k + 1; i++) ans = min(ans, xl[i + n - k - 1] - xl[i]); 24 for (int i = 1; i <= ty - n + k + 1; i++) ans = min(ans, yl[i + n - k - 1] - yl[i]); 25 if (ans == INF) cout << -1; 26 else printf("%.8lf", ans); 27 return 0; 28 }
D、hanayo和米饭
1、链接
https://ac.nowcoder.com/acm/contest/3002/D
2、题面
hanayo很喜欢吃米饭。
有一天,她拿出了 个碗,第一个碗装了 粒米饭,第二个碗装了 粒米饭,以此类推,第 个碗装了 粒米饭。
然而,爱搞恶作剧的rin把所有的碗的顺序打乱,并拿走了一个碗。hanayo想知道,rin拿走的碗里有多少粒米饭?
3、思路
签到题,略。
4、代码
1 #include2 using namespace std; 3 4 typedef long long ll; 5 6 ll n, o, tot; 7 8 int main() { 9 cin >> n; 10 for (int i = 1; i <= n - 1; i++) cin >> o, tot += o; 11 cout << (1 + n) * n / 2 - tot; 12 return 0; 13 }
E、rin和快速迭代
1、链接
https://ac.nowcoder.com/acm/contest/3002/E
2、题面
rin最近喜欢上了数论。
然而数论实在太复杂了,她只能研究一些简单的问题。
这天,她在研究正整数因子个数的时候,想到了一个“快速迭代”算法。设 为 的因子个数,将 迭代下去,rin猜想任意正整数最终都会变成 。
例如: 。
她希望你帮她验证一下。她会给你一个正整数 ,让你输出它在迭代过程中,第一次迭代成 的迭代次数。
3、思路
(本来这也是个签到题吧。。结果我弄了大半天,烦)
虽然数据看着很大,n <= 10 ^ 12,但众所周知求n的约数,只需枚举1到sqrt(n),又根据题意的迭代方式,实际复杂度仅仅只有O(sqrt(n)) = O(10 ^ 6)。然而我首先没注意到如果它是完全平方数的情况,直接枚举完*2,然后就巴拉巴拉弄了好久,人傻了。最近码力明显下滑了。
不过这题又非暴力解法,由“约数个数定理”得:
4、代码
1 #include2 using namespace std; 3 4 typedef long long ll; 5 6 ll n; 7 8 ll work(ll o, ll d) { 9 if (o == 2) return d; 10 ll x = 0; 11 for (ll i = 1; i <= sqrt(o); i++) x += o % i ? 0 : i * i == o ? 1 : 2; 12 return work(x, d + 1); 13 } 14 15 int main() { 16 cin >> n; 17 cout << work(n, 0); 18 return 0; 19 }
F、maki和tree
1、链接
https://ac.nowcoder.com/acm/contest/3002/F
2、题面
有一天,maki拿到了一颗树。所谓树,即没有自环、重边和回路的无向连通图。
这个树有 个顶点, 条边。每个顶点被染成了白色或者黑色。
maki想知道,取两个不同的点,它们的简单路径上有且仅有一个黑色点的取法有多少?
注:
①树上两点简单路径指连接两点的最短路。
② 和 的取法视为同一种。
3、思路
对于每一个黑点,所有与其相连的白色节点连通图相互之间的点可以取路径,取法个数为
可以简化为,以降低复杂度。
4、代码
1 #include2 using namespace std; 3 4 #define MAXN 100005 5 #define INF 1 << 30 6 7 typedef long long ll; 8 9 char s[MAXN]; 10 std :: vector <int> G[MAXN]; 11 ll n, sum, x, y, ans; 12 13 void dfs(int o, int f) { 14 if (s[o] == 'B') return; 15 sum++; 16 for (int i = 0; i < G[o].size(); i++) 17 if (G[o][i] != f) dfs(G[o][i], o); 18 } 19 20 int main() { 21 cin >> n >> s + 1; 22 for (int i = 1; i < n; i++) { 23 cin >> x >> y; 24 G[x].push_back(y); 25 G[y].push_back(x); 26 } 27 for (int i = 1; i <= n; i++) { 28 ll t1 = 0, t2 = 0; 29 if (s[i] == 'B') { 30 for (int j = 0; j < G[i].size(); j++) { 31 sum = 0, dfs(G[i][j], G[i][j]); 32 ans += sum, t1 += sum, t2 += sum * sum; 33 } 34 ans += (t1 * t1 - t2) / 2; 35 } 36 } 37 cout << ans; 38 return 0; 39 }
G、rin和快速迭代
1、链接
https://ac.nowcoder.com/acm/contest/3002/G
2、题面
eli拿到了一个仅由小写字母组成的字符串。
她想截取一段连续子串,这个子串包含至少 个相同的某个字母。
她想知道,子串的长度最小值是多少?
注:所谓连续子串,指字符串删除头部和尾部的部分字符(也可以不删除)剩下的字符串。例如:对于字符串“arcaea”而言,“arc””、“rcae”都是其子串。而“car”、“aa”则不是它的子串。
3、思路
直接对于每个字母记录每一次出现的位置,再枚举距离为k时两个位置差的最小值。
4、代码
1 #include2 using namespace std; 3 4 #define MAXN 200005 5 #define INF 1 << 30 6 7 int a[205][MAXN], ans = INF, n, k; 8 char ch[MAXN]; 9 10 int main() { 11 cin >> n >> k >> ch + 1; 12 for (int i = 1; i <= n; i++) a[ch[i]][++a[ch[i]][0]] = i; 13 for (int i = 'a'; i <= 'z'; i++) { 14 if (!a[i][0]) continue; 15 for (int j = 1; j <= a[i][0] - k + 1; j++) ans = min(ans, a[i][j + k - 1] - a[i][j]); 16 } 17 cout << (ans == INF ? -1 : ans + 1); 18 return 0; 19 }
H、nozomi和字符串
1、链接
https://ac.nowcoder.com/acm/contest/3002/H
2、题面
nozomi看到eli在字符串的“花园”里迷路了,决定也去研究字符串问题。
她想到了这样一个问题:
对于一个 “01”串而言,每次操作可以把 0 字符改为 1 字符,或者把 1 字符改为 0 字符。所谓“01”串,即只含字符 0 和字符 1 的字符串。
nozomi有最多 次操作的机会。她想在操作之后找出一个尽可能长的连续子串,这个子串上的所有字符都相同。
nozomi想问问聪明的你,这个子串的长度最大值是多少?
注: 次操作机会可以不全部用完。
如果想知道连续子串的说明,可以去问问eli,nozomi不想再讲一遍。
3、思路
自己做的时候思路爆炸了,知道是要用个l标记和r标记,来动态调整这个长度,然后在调整过程中记录最大值,写了许久却写不好。看了个题解豁然开朗。
题解给了个专业的词来表示这个方法——尺取法。在丈量一段区间是否合适时,秉承的规则是,在操作次数不超过限制的前提下尽可能区间尽可能长。
根据题意,我们有2种操作的方式——0换成1,1换成0。以0换成1为例,对于下一位为1,直接右移右端点;如果为0,当前还有操作次数,则消耗一个操作次数,右移右端点;如果没有了,则右移左端点直到为0,再右移右端点,以腾出一个操作次数使区间前进来寻找是否存在更大值。
4、代码
1 #include2 using namespace std; 3 4 #define MAXN 200005 5 6 int n, k, ans; 7 char ch[MAXN]; 8 9 int main() { 10 cin >> n >> k >> ch + 1; 11 for (int i = 1; i <= n; i++) ch[i] -= '0'; 12 for (int x = 0; x <= 1; x++) { 13 int l = 1, r = 1, o = 0; 14 for (int i = 1; i <= n; i++) { 15 if (ch[i] == 0) 16 if (o < k) o++, r++; 17 else { 18 while (l <= r && ch[l] == 1) l++; 19 l++, r++; 20 } 21 else r++; 22 ans = max(ans, r - l); 23 } 24 } 25 cout << ans; 26 return 0; 27 }
I、nico和niconiconi
1、链接
https://ac.nowcoder.com/acm/contest/3002/I
2、题面
nico平时最喜欢说的口头禅是niconiconi~。
有一天nico在逛著名弹幕网站"niconico"的时候惊异的发现,n站上居然有很多她的鬼畜视频。其中有一个名为《让nico为你洗脑》的视频吸引了她的注意。
她点进去一看,就被洗脑了:"niconicoh0niconico*^vvniconicoG(vniconiconiconiconiconicoG(vniconico......"
弹幕中刚开始有很多“nico*1 nico*2”等计数菌,但到后面基本上都是“计数菌阵亡”的弹幕了。
nico也想当一回计数菌。她认为:"nico" 计 分,"niconi" 计 分,"niconiconi" 计 分。
她拿到了一个长度为 的字符串,请帮她算出最大计数分数。
注:已被计数过的字符不能重复计数!如"niconico"要么当作"nico"+"nico"计 分,要么当作"niconi"+"co"计 分。
3、思路
简单DP。小时候的我超不理解DP,现在觉得DP这东西好妙。可能我对抽象的东西接受得很慢吧。
状态由三种计分的字符串来转移,具体略。
4、代码
1 #include2 using namespace std; 3 4 #define MAXN 300005 5 typedef long long ll; 6 7 const char s[4][10] = {{}, {'n', 'i', 'c', 'o'}, {'n', 'i', 'c', 'o', 'n', 'i'}, {'n', 'i', 'c', 'o', 'n', 'i', 'c', 'o', 'n', 'i'}}; 8 const ll l[4] = {0, 4, 6, 10}; 9 10 ll n, p[4], f[MAXN]; 11 char ch[MAXN]; 12 13 bool chk(ll o, ll x) { 14 if (o < 0) return 0; 15 for (ll i = o; i <= o + l[x] - 1; i++) 16 if (ch[i] != s[x][i - o]) return 0; 17 return 1; 18 } 19 20 int main() { 21 cin >> n >> p[1] >> p[2] >> p[3] >> ch + 1; 22 for (ll i = 1; i <= n; i++) { 23 for (ll j = 1; j <= 3; j++) 24 if (chk(i - l[j] + 1, j)) f[i] = max(f[i], f[i - l[j] + 1] + p[j]); 25 f[i] = max(f[i], f[i - 1]); 26 } 27 cout << f[n]; 28 return 0; 29 }
持续更新中。。。