绩点
链接:https://www.nowcoder.com/acm/contest/164/A
来源:牛客网
题目描述
小A刚考完大学考试。现在已经出了 门课的成绩,他想自己先算一下这些课的绩点是多少。设第i门课的他拿到的绩点是 ,而这门课的学分是 ,那么他的总绩点用下面的公式计算:
换言之,设 为 的和, 为 与 的乘积的和。那么小A的绩点就是 除以 的值。
输入描述
第一行一个整数
。
接下来
行,每行两个数
和
。
输出描述
输出一行一个实数,表示小A的绩点。输出四舍五入的保留1位小数。
实例1
输入
3
3.7 2
4.0 2
3.7 5
输出
3.8
备注
总共有
个数据点:
第
个数据点,满足所有学科得到的
都相同。
第
个数据点,满足
。
第
个数据点,满足所有学科的
值都相同。
对于所有数据点,都满足
等于
或
。
为不超过
,不小于
的整数。
题解
这道题定位为简单题。前三个点的部分分可以这样做。
第一个点,所有的
都相同,因此直接输出任一一门课的
即可。
第二个点
,只有三门课,暴力输入后手算,为不会数组的同学准备。
第三个数据点,所有科目的学分都是相同的,按照式子计算就与学分无关了,答案就是
的平均数
正解:利用数组与
循环,把所有数据读取进来。再按照给定的求和式子计算。且考察浮点数的计算。
实现
#include <bits/stdc++.h>
using namespace std;
int n;
double gpa[1000];
int sc[1000];
int main()
{
cin >> n;
for(int i = 0;i < n;i ++)
scanf("%lf %d", &gpa[i], &sc[i]);
double ans = 0, sum = 0;
for(int i = 0;i < n;i ++) ans += gpa[i] * sc[i], sum += sc[i];
printf("%.1f\n", ans / sum);
ret
urn 0;
}
巨大的棋盘
链接:https://www.nowcoder.com/acm/contest/164/B
来源:牛客网
题目描述
小A站在一个巨大的棋盘上。这个棋盘可以看成是一个网格图。这个网格图的大小为
。左上角坐标为
,右下角坐标为
。这个棋盘很特别,他每行每列都是一个环。具体来说,当小A站在第一行,他往上走的时候,他会走到第
行,站在第
行往下走会走到第一行。对于第一列和第
列类似。小A在棋盘上可以上下左右走,假设他站在位置
,向上走,会走到
,向下回到
,向左到
,向右到
。注意由于棋盘是循环的,他不会走出这个棋盘。
现在小A有一个固定的行走序列
,代表他每一步走的方向,
代表向上,
代表向下,
代表向左,
代表向右。比如小A一开始在
,棋盘大小为
。行走序列为
。那么他会依次经过
。但小A觉得只走一遍
太无聊,因此他会重复走这个序列
次。比如上面的例子,当
时,真正的行走序列为
。
小A有
个备选的起点位置。他一开始先给定你棋盘大小与行走序列,对于每个起点位置,他想知道,他沿着序列走,最终会走到哪个位置停下。
输入描述
第一行三个整数
。
接下来一行一个字符串
,代表行走序列。注意行走序列在真实走的时候要重复
次。
接下来一个整数
。
接下来
行,每行两个整数
,代表小A的一个备选起点。
输出描述
输出 行,每行两个整数,输出对于这个起点,最后的终点是哪里。
示例1
输入
3 6 4
DUUUDLLLLR
3
3 2
2 5
1 4
输出
2 2
1 5
3 4
备注
20%: |S| * T <= 10^6, q = 1
40%: |S| * T <= 10^6, q <= 10^5
60%: |S|, T <= 10^5, q <= 10^5
100%: 1 <= T,n,m <= 10^9, 1 <= x <= n, 1 <= y <= m. 1<= q, |S| <= 10^5
其中|S|代表S的长度。
题解
部分分分别是直接模拟,
次重复没有用乘法而是用加法,以及每次询问重复运算。
正解:解决这道题,需要知道,一个既然棋盘是循环的,那么只关心一个操作序列在
坐标与
坐
标上走了多少。假设最终走了
与
,可以把他们分别对
取模。
次重复只需要把他们都乘上
即可。对于每次询问,直接坐标加上
与
。
实现
//N * K * S(9,K)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 105;
typedef long long ll;
char s[maxn][1005];
ll has[1005], inc[maxn][15], nw[maxn][15];
int n,l, k, bel[10], ord[maxn], ans;
/*ll Ra()
{
return rand() * rand() * 1ll * rand() * rand(); //WIN
return rand() * 1ll * rand(); //LINUX
}*/
bool cmp(int a,int b)
{
for(int i = 0;i < k;i ++)
if (nw[a][i] < nw[b][i]) return 1; else
if (nw[a][i] > nw[b][i]) return 0;
return 0;
}
int check()
{
for(int i = 1;i <= n;i ++)
{
memset(nw[i], 0, sizeof nw[i]);
for(int j = 0;j < 8;j ++) nw[i][bel[j]] ^= inc[i][j];
ord[i] = i;
}
sort(ord + 1, ord + n + 1, cmp);
int ans = 0, siz = 1;
for(int i = 2;i <= n;i ++)
if (!cmp(ord[i - 1], ord[i]) && !cmp(ord[i], ord[i - 1])) ++ siz; else
ans += siz * (siz - 1) / 2, siz = 1;
ans += siz * (siz - 1) / 2;
return ans;
}
void dfs(int now,int m)
{
if (now >= 8)
{
if (m != k) return;
ans = max(ans, check());
return;
}
for(int i = 1;i <= m;i ++)
bel[now] = i - 1,dfs(now + 1,m);
bel[now] = m, dfs(now + 1, m + 1);
}
int main()
{
freopen("string.in","r",stdin),freopen("string.out","w",stdout);
scanf("%d%d%d", &n, &l, &k);
k = max(1, 8 - k);
for(int i = 1;i <= n;i ++) scanf("%s", s[i] + 1);
for(int i = 1;i <= l;i ++)
has[i] = Ra();
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= l;j ++) inc[i][s[i][j] - 'a'] ^= has[j];
dfs(0,0);
printf("%d\n", ans);
return 0;
}
括号
链接:https://www.nowcoder.com/acm/contest/164/C
来源:牛客网
题目描述
小A有一个只包含左右括号的字符串
。但他觉得这个字符串不够美观,因为它不是一个合法的括号串。一个合法的括号串是这样定义的:
1.
是合法的括号串。
2. 若
是合法的括号串,则
则是合法的括号串。
3. 若
是合法的括号串,则
也是合法的括号串。
小A现在希望删掉
中若干个字符,使得剩下的字符串是一个合法的括号串。小A想知道有多少不同的方案。两个方案是不同的,当且仅当他们删除的位置不同。比如当
是
时,有两种方案。分别是删掉第一个位置,或是删掉第二个位置。
输入描述
第一行一个整数
,代表
的长度。
第二行输入
个字符,字符要么是
,要么是
。代表串
。
输出描述
一行一个整数,代表不同的方案数。答案对 取模。
示例1
输入
8
)(()(())
输出
30
备注
。
。
。
。
题解
这题的第一档部分分是暴力枚举删掉哪些括号,然后判断是否是合法的括号序列。这个可以使用栈来做,每次遇到一个右括号则弹出左括号,遇到左括号则加入栈中,不合法当且仅当
最后有多余的左括号或中途不够左括号弹出。
正解:可以发现只关心中间每一步有多少左括号。可以使用
。设
表示考虑到第
个位置,当前选取的子序列(或删剩的括号序列)结尾恰好为第
个位置。左括号数量为
。那么可以枚举上一个位置
, 以及对应的左括号数量
。因为
位置必须考虑,则若
为右括号,则
,否则
。但
要
。最后的答案就是
的和。这个
的复杂度是
的。
但是可以把
位置不选也加入转移,那么就是
。复杂度
。使用滚动数
组优化空间即可。
配对
链接:https://www.nowcoder.com/acm/contest/164/D
来源:牛客网
题目描述
小A有
个长度都是
的字符串。这些字符串只包含前
个小写字符,
。但这些字符串非常的混乱,它们几乎长得互不相同。小A想通过一些规则,让它们长得尽可能相同。小A现在有
次机会,他可以每次机会,可以选择一对字符
,让
变成等价的字符(注意这里
和字符
不是一样的,只是个代号)。注意,等价关系是有传递性的。比如小A让
和
等价,
和
等价,那么
和
等价。
对于两个长度字符串
是等价的,当且仅当对于每一位,
的字符和
的字符都是等价的。
小A希望你告诉他,要怎么利用好这
次机会(当然可以不用完),使得尽可能多对字符串是等价的。注意每对字符串只能算一次。
输入描述
第一行输入三个整数
。
接下来
行,每行给出一个长度为
的字符串。
输出描述
输出一行一个整数,代表最多可能的等价的字符串对。
示例1
输入
5 4 2
ccdd
babd
bdcd
ccda
bacd
输出
4
备注
数据包含
个数据点。每个数据点可能有不同的特性。
对于第
个数据点: 保证每个字符串只包含前
个小写字母
对于第
个数据点:每个字符串都只包含一种字母。
对于第
个数据点:
。
对于所有数据,满足:
,每个字符串只包含前8个小写字母。
题解
正解:直接枚举选择哪些字母配对很慢,可以考虑枚举联通块。当 时,显然所有串都能相同了,因为只需要 条边就能让所有字母等价。因此考虑枚举字母的联通块情况。而贪心地想, 肯定用完最好,因此联通块情况就只有 种情况,其中 为第二类斯特林数。枚举完联通块情况后,每个字符串中,每种字符替代为对应联通块编号,变成等价的字符串。然后计算哈希排序算等价对数。但这样可能超时。可以按字母分别维护每个字母出现位置的哈希值,重新算时复杂度是 ,而不是 的。
实现
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 110;
const int maxm = 1010;
char s[maxn][maxm];
ll has[maxm], inc[maxn][15], nw[maxn][15];
int n, l, k, bel[10], ord[maxn], ans;
ll Ra() {
return rand() * rand() * 1ll * rand() * rand(); //WIN
return rand() * 1ll * rand(); //LINUX
}
bool cmp(int a, int b) {
for(int i = 0; i < k; i++) {
if(nw[a][i] < nw[b][i]) {
return 1;
} else if(nw[a][i] > nw[b][i]) {
return 0;
}
}
return 0;
}
inline int check() {
for(int i = 1; i <= n; i++) {
memset(nw[i], 0, sizeof(nw[i]));
for(int j = 0; j < 8; j++) {
nw[i][bel[j]] ^= inc[i][j];
}
ord[i] = i;
}
sort(ord + 1, ord + n + 1, cmp);
int ans = 0, siz = 1;
for(int i = 2; i <= n; i++) {
if(!cmp(ord[i-1], ord[i]) && !cmp(ord[i], ord[i-1])) {
siz++;
} else {
ans += siz * (siz - 1) / 2, siz = 1;
}
}
ans += siz * (siz - 1) / 2;
return ans;
}
inline void dfs(int now, int m) {
if(now >= 8) {
if(m != k) return ;
ans = max(ans, check());
return ;
} else {
for(int i = 1; i <= m; i++){
bel[now] = i - 1;
dfs(now + 1, m);
}
bel[now] = m;
dfs(now + 1, m + 1);
}
}
int main() {
scanf("%d%d%d", &n, &l, &k);
k = max(1, 8 - k);
for(int i = 1; i <= n; i++) {
scanf("%s", s[i] + 1);
}
for(int i = 1; i <= l; i++) {
has[i] = Ra();
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= l; j++) {
inc[i][s[i][j] - 'a'] ^= has[j];
}
}
dfs(0, 0);
printf("%d\n", ans);
return 0;
}