F. Foreign Football

一共有\(n\)支队伍,每支队伍的名称为\(s_i\),给定一个\(n \times n\)的矩阵,\(a_{i,j}\)代表第\(i\)支队伍和第\(j\)支队伍名字的拼接,即\(a_{i,j}=s_i+s_j\),请你通过该矩阵推断出所有队伍的名称,如果有多个解,输出MANY,如果有唯一解,输出UNIQUE,并输出所有队伍的名称,如果无解,输出NONE

注意:队伍名称非空

\(2 \leq n \leq 500,\sum a_{i,j} \leq 1e6\)

题解:高斯消元 + 字符串哈希

  • 如果\(n \geq 3\),显然我们可以对\(s_1+s_2,s_2+s_3,s_1+s_3\)这三个方程进行高斯消元,如果有唯一解我们就可以得到\(s_1\)的长度,那么我们就能够通过\(s_1\)来确定所有队伍的名称,最后枚举一遍矩阵进行检查,如果检查不通过,输出NONE
  • 对于\(n = 2\)的情况,我们考虑特判,我们不妨枚举\(s_1\)的长度后得到\(s_1,s_2\),通过字符串哈希\(O(1)\)检查\(a_{1,2}\)和\(a_{2,1}\)能否对应即可,如果能够多次对应,那么输出MANY,其他情况对应输出即可
int n;
string s[N][N], ans[N];
double a[10][10];
int h1[M], h2[M], p[M], base = 131;
int get_hash1(int l, int r) { return ((h1[r] - h1[l - 1] * p[r - l + 1] % mod) % mod + mod) % mod; }
int get_hash2(int l, int r) { return ((h2[r] - h2[l - 1] * p[r - l + 1] % mod) % mod + mod) % mod; } int Gauss(int n, int m) // n行m列的增广矩阵
{
int r = 0; // 增广矩阵的秩
for (int j = 1; j <= m - 1; ++j) // 枚举系数矩阵的列
{
int mx = r + 1; // 当前所在行号
for (int i = mx; i <= n; ++i) // 枚举行来找到该列绝对值最大非0系数
{
if (fabs(a[i][j]) > fabs(a[mx][j]))
mx = i;
} if (fabs(a[mx][j]) < eps) // 对角线上主元素为0,没有唯一解,矩阵的秩不用加1
continue; for (int i = 1; i <= m; ++i) // 将这最大系数这一行和对角线那一行进行交换
swap(a[r + 1][i], a[mx][i]); for (int i = m; i >= j; --i) // 将这一行的主元素系数变为1
a[r + 1][i] /= a[r + 1][j]; for (int i = 1; i <= n; ++i) // 消去主元所在列的其他行的主元
{
if (i != r + 1)
{
double t = a[i][j] / a[r + 1][j];
for (int k = 1; k <= m; ++k)
a[i][k] -= t * a[r + 1][k];
}
}
r++;
} if (r < m - 1) // 矩阵的秩小于未知量的个数(m - 1)
{
for (int i = r + 1; i <= n; ++i)
{
if (fabs(a[i][m]) > eps)
return 0; // 无解(增广矩阵和系数矩阵的秩不同)
}
return 2; // 有无穷多组解
}
return 1;
} void solve()
{
cin >> n;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
cin >> s[i][j];
if (n == 2)
{
int len1 = s[1][2].length(), len2 = s[2][1].length();
string tmp = s[1][2];
if (len1 != len2 || len1 <= 1)
{
cout << "NONE" << endl;
return;
}
s[1][2] = " " + s[1][2], s[2][1] = " " + s[2][1];
p[0] = 1;
for (int i = 1; i <= len1; ++i)
h1[i] = (h1[i - 1] * base % mod + s[1][2][i]) % mod;
for (int i = 1; i <= len1; ++i)
h2[i] = (h2[i - 1] * base % mod + s[2][1][i]) % mod;
for (int i = 1; i <= max(len1, len2); ++i)
p[i] = p[i - 1] * base % mod;
int cnt = 0, p = 0;
for (int i = 1; i <= len1 - 1; ++i)
{
if (get_hash1(1, i) == get_hash2(len1 - i + 1, len1) && get_hash1(i + 1, len1) == get_hash2(1, len1 - i))
cnt++, p = i;
}
if (cnt == 0)
cout << "NONE" << endl;
else if (cnt == 1)
{
cout << "UNIQUE" << endl;
cout << tmp.substr(0, p) << endl;
cout << tmp.substr(p) << endl;
}
else
cout << "MANY" << endl;
return;
}
a[1][4] = (int)s[1][2].size();
a[2][4] = (int)s[2][3].size();
a[3][4] = (int)s[1][3].size();
a[1][1] = 1, a[1][2] = 1, a[1][3] = 0;
a[2][1] = 0, a[2][2] = 1, a[2][3] = 1;
a[3][1] = 1, a[3][2] = 0, a[3][3] = 1;
int res = Gauss(3, 4);
if (!res)
{
cout << "NONE" << endl;
return;
}
else if (res == 2)
{
cout << "MANY" << endl;
return;
}
else
{
for (int i = 1; i <= 3; ++i)
{
if (a[i][4] <= 0)
{
cout << "NONE" << endl;
return;
}
}
ans[1] = s[1][2].substr(0, (int)a[1][4]);
for (int i = 2; i <= n; ++i)
ans[i] = s[1][i].substr((int)ans[1].size());
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
{
if (i == j)
continue;
if ((ans[i] + ans[j]) != s[i][j])
{
cout << "NONE" << endl;
return;
}
}
}
}
cout << "UNIQUE" << endl;
for (int i = 1; i <= n; ++i)
cout << ans[i] << endl;
}

G. Graduation Guarantee

\(1 \leq k \leq n \leq 5000\)

题解:概率\(DP\)

  • 显然我们贪心的考虑一定先回答正确率高的题目,所以考虑根据题目的正确率降序排列

  • 我们设计状态:\(dp[i][j]\)代表只从前\(i\)个题目中选择题目回答,得到\(j\)分的概率

  • 考虑转移:

\(dp[0][0] = 1\)

\(dp[i][j] = dp[i-1][j-1] * p[i] + dp[i-1][j+1]*(1 - p[i])\)

注意:得分可能是负的,所以我们枚举第二维的时候需要从\(-n\)开始,加个偏移量即可

  • 最后答案就是对所有得分超过\(k\)的状态取个\(max\)即可
int n, k;
double dp[N][N << 1], p[N]; void solve()
{
cin >> n >> k;
for (int i = 1; i <= n; ++i)
cin >> p[i];
sort(p + 1, p + n + 1, greater<double>());
dp[0][N] = 1;
for (int i = 1; i <= n; ++i)
for (int j = N - n; j <= n + N; ++j)
dp[i][j] = dp[i - 1][j - 1] * p[i] + dp[i - 1][j + 1] * (1 - p[i]);
double ans = 0;
for (int i = 1; i <= n; ++i)
{
double res = 0;
for (int j = k + N; j <= n + N; ++j)
res += dp[i][j];
ans = max(ans, res);
}
cout << fixed << setprecision(6) << ans << endl;
}

K. Keyboard Queries

给定一个长度为\(n\),但是未知的字符串\(s\),给定\(q\)次询问:

  • \(1 , l, r\):告诉你\([l,r]\)的子串是回文串
  • \(2,a,b,x,y\):询问\([a,b]\)和\([x,y]\)区间的子串是否一样,存在\(3\)种回答:一样,不一样,不知道

题解:启发式合并并查集 + 线段树维护字符串哈希

  • 显然需要线段树维护正串哈希和反串哈希,首先\(build\)的时候保证每个叶子节点的\(hash\)值不一样
  • 对于第一个操作,我们可以知道哪些位置的字符是一样的,所以我们考虑通过并查集连边,但是如果我们暴力连边的话会超时,所以我们考虑只对还没有确定位置连边,那么我们最多只会连\(n-1\)条边
  • 所以我们现在的问题就是对于一个询问,我们需要快速找到\([l,r]\)中第一个没有确定的位置进行连边(不确定的位置代表该位置对称的两侧的字符不相同),我们考虑通过二分来实现,因为我们通过线段树维护了哈希,所以我们可以二分出第第一个不相等的位置,例如\(abcdecba\)中第一个不相等的位置为\(4\),\(check\)时我们只要比较前缀的正串哈希和后缀的反串哈希是否相同即可,复杂度\(O(nlog^2n)\)
  • 我们考虑连边的时候利用并查集启发式合并即可,即将\(sz\)小的合并给\(sz\)大的,那么合并的同时在线段树上单点修改\(hash\)即可
  • 对于询问\(2\),一个显然的回答,如果长度不一样,那么一定不相同,如果在线段树上查询出相同,那么一定相同,否则就是不知道
  • 复杂度:\(O(nlog^2n + qlogn)\)
#include <bits/stdc++.h>
using namespace std;
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
typedef unsigned long long ULL;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 1e5 + 10, M = 4e5 + 10; int n, q, fa[N], sz[N], pw[N], base = 131;
vector<int> vec[N]; struct info
{
int hash, rhash, len;
info(int hash = 0, int rhash = 0, int len = 0) : hash(hash), rhash(rhash), len(len) {}
friend info operator+(const info &a, const info &b)
{
info c;
c.len = a.len + b.len;
c.hash = (a.hash * pw[b.len] % mod + b.hash) % mod;
c.rhash = (b.rhash * pw[a.len] % mod + a.rhash) % mod;
return c;
}
};
struct SEG
{
info val;
} seg[N << 2];
void up(int id) { seg[id].val = seg[lson].val + seg[rson].val; }
void build(int id, int l, int r)
{
if (l == r)
{
seg[id].val = info(l, l, 1);
return;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
up(id);
}
void change(int id, int l, int r, int x, int v)
{
if (l == r)
{
seg[id].val.hash = seg[id].val.rhash = v;
return;
}
int mid = l + r >> 1;
if (x <= mid)
change(lson, l, mid, x, v);
else
change(rson, mid + 1, r, x, v);
up(id);
}
info query(int id, int l, int r, int ql, int qr)
{
if (ql <= l && r <= qr)
return seg[id].val;
int mid = l + r >> 1;
if (qr <= mid)
return query(lson, l, mid, ql, qr);
else if (ql > mid)
return query(rson, mid + 1, r, ql, qr);
else
return query(lson, l, mid, ql, qr) + query(rson, mid + 1, r, ql, qr);
} void merge(int x, int y)
{
int fx = fa[x], fy = fa[y];
if (fx != fy)
{
if (sz[fx] > sz[fy])
swap(fx, fy);
for (auto u : vec[fx])
{
fa[u] = fy;
change(1, 1, n, u, fy);
vec[fy].push_back(u);
}
sz[fy] += sz[fx];
fa[fx] = fy;
vec[fx].clear();
}
} void solve()
{
cin >> n >> q;
pw[0] = 1ll;
for (int i = 1; i <= n; ++i)
{
fa[i] = i;
sz[i] = 1ll;
pw[i] = pw[i - 1] * base % mod;
vec[i].push_back(i);
}
build(1, 1, n);
while (q--)
{
int op, L, R, a, b, x, y;
cin >> op;
if (op == 1)
{
cin >> L >> R;
int Mid1 = L + R >> 1, Mid2 = L + R + 1 >> 1;
auto check = [&](int l1, int r1, int l2, int r2)
{
return query(1, 1, n, l1, r1).hash == query(1, 1, n, l2, r2).rhash;
};
while (!check(L, Mid1, Mid2, R))
{
int l = 1, r = Mid1;
while (l <= r)
{
int mid = l + r >> 1;
if (check(L, L + mid - 1, R - mid + 1, R))
l = mid + 1;
else
r = mid - 1;
}
merge(L + r, R - r);
}
}
else
{
cin >> a >> b >> x >> y;
if (b - a + 1 != y - x + 1)
cout << "Not equal" << endl;
else if (query(1, 1, n, a, b).hash == query(1, 1, n, x, y).hash)
cout << "Equal" << endl;
else
cout << "Unknown" << endl;
}
}
}
signed main(void)
{
Zeoy;
int T = 1;
// cin >> T;
while (T--)
solve();
return 0;
}

2022-2023 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2022)的更多相关文章

  1. (寒假GYM开黑)2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)

    layout: post title: 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) author: &qu ...

  2. Codeforces Gym101572 B.Best Relay Team (2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017))

    2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017) 今日份的训练,题目难度4颗星,心态被打崩了,会的算法太少了,知 ...

  3. 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)- D. Delivery Delays -二分+最短路+枚举

    2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)- D. Delivery Delays -二分+最短路+枚举 ...

  4. 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)-E. Explosion Exploit-概率+状压dp

    2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)-E. Explosion Exploit-概率+状压dp [P ...

  5. 2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017) Solution

    A - Airport Coffee 留坑. B - Best Relay Team 枚举首棒 #include <bits/stdc++.h> using namespace std; ...

  6. 2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017)

    A. Airport Coffee 设$f_i$表示考虑前$i$个咖啡厅,且在$i$处买咖啡的最小时间,通过单调队列优化转移. 时间复杂度$O(n)$. #include<cstdio> ...

  7. Codeforces Gym101572 G.Galactic Collegiate Programming Contest (2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017))

    Problem G Galactic Collegiate Programming Contest 这个题题意读了一会,就是几个队参加比赛,根据实时的信息,问你1号队的实时排名(题数和罚时相同的时候并 ...

  8. 模拟赛小结:2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017)

    比赛链接:传送门 本场我们队过的题感觉算法都挺简单的,不知道为啥做的时候感觉没有很顺利. 封榜后7题,罚时1015.第一次模拟赛金,虽然是北欧的区域赛,但还是有点开心的. Problem B Best ...

  9. ACM ICPC, JUST Collegiate Programming Contest (2018) Solution

    A:Zero Array 题意:两种操作, 1 p v  将第p个位置的值改成v  2  查询最少的操作数使得所有数都变为0  操作为可以从原序列中选一个非0的数使得所有非0的数减去它,并且所有数不能 ...

  10. ACM ICPC, Amman Collegiate Programming Contest (2018) Solution

    Solution A:Careful Thief 题意:给出n个区间,每个区间的每个位置的权值都是v,然后找长度为k的区间,使得这个区间的所有位置的权值加起来最大,输出最大权值, 所有区间不重叠 思路 ...

随机推荐

  1. 日志与追踪的完美融合:OpenTelemetry MDC 实践指南

    前言 在前面两篇实战文章中: OpenTelemetry 实战:从零实现分布式链路追踪 OpenTelemetry 实战:从零实现应用指标监控 覆盖了可观测中的指标追踪和 metrics 监控,下面理 ...

  2. [JS设计模式]:策略模式及应用-计算奖金、表单验证的实现(5)

    介绍 策略模式的意义是定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.此模式让算法的变化不会影响到使用算法的客户. 实现 举一个例子,比如我们做数据合法性校验,一般是通过swich来实现 ...

  3. JavaScript – ES6-ES2023 大杂烩

    前言 一年半没有写 JS 了, 今天开始来个大复习, 顺便把这么多年零零散散的知识点做成笔记. 练练字. ES 3, 5, 6, 2017, 2018... ES 6 等于 ES2015 ES 7 等 ...

  4. Asp.net core 学习笔记之 authen + autho + oidc + oauth + spa 第九篇 (external login)

    External login 就是指通过 Google, Microsoft, Facebook account 做登入. https://docs.microsoft.com/en-us/aspne ...

  5. ASP.NET Core – Case Style Conversion

    前言 之前就有写过一篇 <<前后端沟通 naming conversion 转换需要知道的事>> 这篇做一个总结整理. 我们知道 C# 的 Property 是 PascalC ...

  6. Java!!冲

    开始学习Java!!!

  7. 大一下的acm生活

    在一个名气不大的211学校刷题的日常. 感觉这些算法题好难啊! 最近有好多实验室要招新,不知道该怎么办,自己只想就业,并不想升学,好烦! 真枯燥,好无聊. 现在要学习相关的网页设计和网站建设,例如配色 ...

  8. React的useId,现在Vue3.5终于也有了!

    前言 React在很早之前的版本中加了useId,用于生成唯一ID.在Vue3.5版本中,终于也有了期待已久的useId.这篇文章来带你搞清楚useId有哪些应用场景,以及他是如何实现的. 关注公众号 ...

  9. AD域下,普通用户环境下安装软件需要管理员权限的解决办法

    原理:将AD域普通用户添加到管理组中,使其拥有管理员权限 做法: 1.切换到AD域管理员账户: 2.计算机 -> 管理 -> 用户和组 -> 组 -> Administrato ...

  10. MySQL9的3个新特性

    本文讲解MySQL9的3个新特性:支持将JSON输出保存到用户变量.支持准备语句以及支持面向AI的向量存储. 17.12  MySQL9新特性1--支持将JSON输出保存到用户变量 从MySQL 9版 ...