A - Wrong Answer
构造一个0…9内的
c
c
c
使得
a
+
b
≠
c
a+b \neq c
a+b=c
方法:
a,b都为0时取1
否则取0
B - Adjacency Matrix
图的01矩阵,求每个点 u u u直接连接的点
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
int a[101][101];
int n;
int main(){
//freopen("in.txt", "r", stdin);
cin >> n;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
cin >> a[i][j];
if (a[i][j]) printf("%d ", j);
}
printf("\n");
}
return 0;
}
C - 343
求小于N的最大立方回文数
暴力
D - Diversity of Scores
N个数一开始都是0
给出T次修改,每次可以对位置i的数修改成
A
i
A_i
Ai
最后求修改完之后有几个不同的值
维护每个数出现的次数,1变为0时和从0变为1时要更新结果
E - 7x7x7
构造一种摆放方式,摆放三个777的立方体,在空间中占据的格子被一个立方体占据的体积为 V 1 V_1 V1,两个立方体占据的体积为 V 2 V_2 V2,三个为 V 3 V_3 V3
很bt的题,搞不出来。
题解是暴力做,还没看懂。
F - Second Largest Query
单点更新,区间查询第二小的数出现的个数
看到这种描述就知道是线段树
贴一下我的代码 719ms,里面用了个set排序,维护第二小的个数比较麻烦。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
const int N = 200020;
struct Node {
int l, r;
int mx, smx; // max, second max
int mxcnt, smxcnt;
};
Node tr[N << 2];
int n, q;
int a[N];
const int inf = 1 << 29;
#define lu (u * 2)
#define ru (u * 2 + 1)
void push_up(int u) {
int x0 = tr[lu].mx, x1 = tr[lu].smx;
int y0 = tr[ru].mx, y1 = tr[ru].smx;
set<int, greater<int>> res;
res.insert(x0); res.insert(x1); res.insert(y0); res.insert(y1);
auto it = res.begin();
tr[u].mx = *it;
it++;
tr[u].smx = *it;
tr[u].mxcnt = tr[u].smxcnt = 0;
if (tr[u].mx == x0) tr[u].mxcnt += tr[lu].mxcnt;
if (tr[u].mx == y0) tr[u].mxcnt += tr[ru].mxcnt;
if (tr[u].smx == x0) tr[u].smxcnt += tr[lu].mxcnt;
if (tr[u].smx == y0) tr[u].smxcnt += tr[ru].mxcnt;
if (tr[u].smx == x1) tr[u].smxcnt += tr[lu].smxcnt;
if (tr[u].smx == y1) tr[u].smxcnt += tr[ru].smxcnt;
}
void build(int l, int r, int u) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].mx = a[l];
tr[u].smx = 0;
tr[u].mxcnt = 1, tr[u].smxcnt = 1;
return;
}
int mi = (l + r) / 2;
build(l, mi, lu);
build(mi + 1, r, ru);
push_up(u);
}
void update(int p, int u, int v) {
if (tr[u].l == tr[u].r && tr[u].l == p) {
tr[u].mx = v;
tr[u].smx = 0;
tr[u].mxcnt = 1, tr[u].smxcnt = 1;
return;
}
int mi = (tr[u].l + tr[u].r) / 2;
if (p <= mi) {
update(p, lu, v);
}
else {
update(p, ru, v);
}
push_up(u);
}
Node query(int l, int r, int u) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u];
}
int mi = (tr[u].l + tr[u].r) / 2;
set<int, greater<int>> res;
if (r <= mi) {
Node t = query(l, r, lu);
return t;
}
if (l > mi) {
Node t = query(l, r, ru);
return t;
}
Node lt = query(l, r, lu), rt = query(l, r, ru);
Node ret{ 0, 0 };
int x0 = lt.mx, x1 = lt.smx;
int y0 = rt.mx, y1 = rt.smx;
res.insert(x0); res.insert(x1); res.insert(y0); res.insert(y1);
auto it = res.begin();
ret.mx = *it;
it++;
ret.smx = *it;
ret.mxcnt = ret.smxcnt = 0;
if (ret.mx == x0) ret.mxcnt += lt.mxcnt;
if (ret.mx == y0) ret.mxcnt += rt.mxcnt;
if (ret.smx == x0) ret.smxcnt += lt.mxcnt;
if (ret.smx == y0) ret.smxcnt += rt.mxcnt;
if (ret.smx == x1) ret.smxcnt += lt.smxcnt;
if (ret.smx == y1) ret.smxcnt += rt.smxcnt;
return ret;
}
int main(){
//freopen("in.txt", "r", stdin);
cin >> n >> q;
for (int i = 1; i <= n; ++i) cin >> a[i];
build(1, n, 1);
for (int i = 1; i <= q; ++i) {
int op;
cin >> op;
if (op == 1) {
int p, x;
cin >> p >> x;
update(p, 1, x);
}
else {
int l, r;
cin >> l >> r;
Node ret = query(l, r, 1);
int ans = ret.smx == 0 ? 0 : ret.smxcnt;
printf("%d\n", ans);
}
}
return 0;
}
G - Compress Strings
题目很简单,有
N
N
N个
(
N
≤
20
)
(N \leq 20)
(N≤20)字符串
求最短的字符串
s
s
s能全部包含这些字符串
首先把那些互相包含在内的情况去掉。
最短的情况一定是前面字符串的后缀和后面的字符串前缀相同。
问题就是求一个1…N的排列,使得拼接完成后的总长度最短。这是个典型的排列状态dp问题。
现在的难点是两个字符串拼接后最短的新字符串长度。换言之需要求前面字符串的后缀和后面字符串的前缀最大相同的长度。
例如
| snuke | kensho |
最大相同长度是2
我们可以把两个字符串反着拼起来:kenshosnuke,如何求这个ke?
如果熟悉kmp算法,会发现ke就是next数组想要求的东西。
但是要注意不需要完全拼成字符串,因为最大的相同长度不会超过两个字符串长度的最小值。
而且你的答案不会超过两个字符串长度的最大值,所以在中间我们加个特殊字符:
kensh#snuke
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
int f[21][21];
int n;
string ss[21];
vector<string> used;
bool v[21];
vector<vi> ns;
int g[1 << 20][20];
vi get_nxt(const string& s) {
int n = s.length();
vi nxt(n + 1, 0);
for (int i = 1; i < n; ++i) {
int j = nxt[i - 1];
if (s[i] == s[j]) nxt[i] = j + 1;
else {
while (j) {
j = nxt[j - 1];
if (s[i] == s[j]) {
nxt[i] = j + 1;
break;
}
}
}
}
return nxt;
}
bool contain(int i, int j, const vi &nxt) {
if (ss[i].size() < ss[j].size()) return false;
const string& s = ss[i];
const string& t = ss[j];
int n = s.size(), m = t.size();
i = 0, j = 0;
while (i < n) {
if (s[i] == t[j]) {
i++, j++;
}
else {
if (j == 0) i++;
else {
j = nxt[j - 1];
}
}
if (j == m)
return true;
}
return false;
}
void con(int i, int j) {
int n = used[i].length(), m = used[j].length();
int l = min(n, m);
string si, sj;
if (n > l) {
si = used[i].substr(0, l);
}
else {
si = used[i];
}
if (m > l) {
sj = used[j].substr(m - l, l);
}
else {
sj = used[j];
}
string s = si + '#' + sj;
vi nxt = get_nxt(s);
n = s.size();
f[j][i] = nxt[n - 1];
}
int main(){
//freopen("in.txt", "r", stdin);
cin >> n;
set<string> sset;
for (int i = 0; i < n; ++i) {
string t;
cin >> t;
sset.insert(t);
}
// 完全相同的情况先处理
n = sset.size();
int idx = 0;
for (string s : sset) {
ss[idx++] = s;
}
for (int i = 0; i < n; ++i) {
vi nxt = get_nxt(ss[i]);
for (int j = 0; j < n; ++j) {
if (i != j && contain(j, i, nxt)) {
v[i] = 1;
break;
}
}
}
// 被包含的字符串不算
for (int i = 0; i < n; ++i) {
if (!v[i]) used.push_back(ss[i]);
}
n = used.size();
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (i != j) {
con(i, j);
}
}
}
memset(g, 0x33, sizeof(g));
for (int i = 0; i < n; ++i)
g[1 << i][i] = used[i].size();
for (int m = 1; m < (1 << n); ++m) {
for (int j = 0; j < n; ++j) {
if ((m >> j) & 1) {
for (int i = 0; i < n; ++i) {
if ((m >> i) & 1) continue;
int nm = m | (1 << i);
g[nm][i] = min(g[nm][i], g[m][j] + int(used[i].size()) - f[j][i]);
// 这里忘记改成 used[i].size()了,原来是ss[i].size(),WA了两发
}
}
}
}
int msk = (1 << n) - 1;
int ans = g[msk][0];
for (int i = 0; i < n; ++i) ans = min(g[msk][i], ans);
printf("%d\n", ans);
return 0;
}
有同学拿hash来计算最大相同长度,也是不错的思路