前情提要:
AC ABCDEG,首次拿下金名表现分。如果 F 能及时调出来那就更好了。
F 会单开一篇文章,如果你下面的思路没有看懂,看代码也没有关系。
给你一个长度为 N N N 的正整数序列: A = ( A 1 , A 2 , … , A N ) A=(A_1,A_2,\dots,A_N) A=(A1,A2,…,AN) .
求 A A A 的奇数索引元素之和。即求出 A 1 + A 3 + A 5 + ⋯ + A m A_1 + A_3 + A_5 + \dots + A_m A1+A3+A5+⋯+Am ,其中 m m m 是不超过 N N N 的最大奇数。
直接使用 for 循环即可。
#include
using namespace std;
const int N = 110;
int a[N];
int n;
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
int ans = 0;
for (int i = 1; i <= n; i += 2)
ans += a[i];
cout << ans << endl;
return 0;
}
给你一个由小写英文字母和?
组成的字符串 T T T 和一个由小写英文字母组成的字符串 U U U 。
字符串 T T T 是由某个只有小写字母的字符串 S S S ,把里面的恰好四个 ?
替换成小写字母得到的。
判断原始字符串 S S S 是否可能包含作为连续子串的 U U U 。
?
。?
。可能是最无脑的做法?
考虑直接枚举 T T T 四个 ?
填入的字母以得到 S S S,共 2 6 4 26^4 264 种情况。然后再暴力找子串是否有 U U U 即可。复杂度 O ( 能过 ) O(能过) O(能过)。
#include
using namespace std;
string t, u;
int pos[4];
int main() {
cin >> t >> u;
int nw = 0;
for (int i = 0; i < (int)t.size(); i++)
if (t[i] == '?')
pos[nw++] = i;
for (char a = 'a'; a <= 'z'; a++)
for (char b = 'a'; b <= 'z'; b++)
for (char c = 'a'; c <= 'z'; c++)
for (char d = 'a'; d <= 'z'; d++) {
t[pos[0]] = a, t[pos[1]] = b, t[pos[2]] = c, t[pos[3]] = d;
for (int i = 0; i + u.size() - 1 < (int)t.size(); i++) {//找子串
if (t.substr(i, (int)u.size()) == u) {
cout << "Yes\n";
return 0;
}
}
}
cout << "No\n";
return 0;
}
WAtCoder 上有 N N N 个用户,编号从 1 1 1 到 N N N ;有 M M M 个竞赛页面,编号从 1 1 1 到 M M M 。最初,没有用户拥有查看任何竞赛页面的权限。
你会收到 Q Q Q 个查询,需要按顺序处理。每个查询都属于以下三种类型之一:
1 X Y
:授予用户 X X X 查看竞赛页面 Y Y Y 的权限。2 X
:授予用户 X X X 查看所有比赛页面的权限。3 X Y
:回答用户 X X X 是否可以查看比赛页面 Y Y Y 。一个用户有可能多次被授予查看同一个比赛页面的权限。
第一个操作和第三个查询都是可以使用 pair 和 set 解决掉的,而第二个查询直接记下来即可。因为操作中没有删除权限的操作,所以这样是可以的。
#include
using namespace std;
set<pair<int, int> > st;
const int N = 200010;
bool f[N];
int main() {
int n, m, q;
cin >> n >> m >> q;
while (q--) {
int op;
cin >> op;
if (op == 1) {
int x, y;
cin >> x >> y;
st.insert({x, y});
} else if (op == 2) {
int x;
cin >> x;
f[x] = 1;
} else {
int x, y;
cin >> x >> y;
if (f[x] || st.find({x, y}) != st.end()) cout << "Yes\n";
else
cout << "No\n";
}
}
return 0;
}
给你一个长度为 N N N 的整数序列 A = ( A 1 , A 2 , … , A N ) A=(A_1,A_2,\dots,A_N) A=(A1,A2,…,AN) 和一个非负整数 D D D 。我们希望从 A A A 中删除尽可能少的元素,得到满足以下条件的序列 B B B :
求最少需要删除的元素个数。
首先特判 D = 0 D=0 D=0 的情况,显然就是每一种数出现次数然后 − 1 -1 −1 的和。
然后就是 D ≠ 0 D \not = 0 D=0 的情况。首先把 A A A 所有元素去重,把每一种数去重前的出现次数记录下来。
然后就是熟悉的图论建模方式:对于 A i A_i Ai,如果 A i + D A_i + D Ai+D 出现在了 A A A 里面,则连 A i → A i + D A_i \to A_i+D Ai→Ai+D 的一条边。 代表这两个东西不能同时存在。
显然最终会形成一条链(边的数量可能是 0 0 0),而且这条链上面的所有边的两端至少都有一种数要被赶尽杀绝(不妨设每一个点的点权都是这个数在原数组种出现的次数)。
但是这个时候不能单纯的黑白染色,可以在链上跑线性 d p dp dp。 f i f_i fi 表示选了第 i i i 个,则显然有 f i = min ( f i − 1 , f i − 2 ) + v a l i f_i = \min(f_{i-1},f_{i-2})+val_i fi=min(fi−1,fi−2)+vali。复杂度为 O ( N + V ) O(N+V) O(N+V),可以通过。
#include
#define int long long
using namespace std;
int n, d;
const int N = 500010;
int a[N];
int cnt[N * 5];
int to[N];
map<int, int> mp;
bool f[N];
vector<int> v, dp;
signed main() {
cin >> n >> d;
for (int i = 1; i <= n; i++)
cin >> a[i], cnt[a[i]]++;
sort(a + 1, a + n + 1);
n = unique(a + 1, a + n + 1) - a - 1;
for (int i = 1; i <= n; i++)
mp[a[i]] = i;
if (d == 0) {
int ans = 0;
for (int i = 1; i <= n; i++)
ans += cnt[a[i]] - 1;
cout << ans << endl;
return 0;
}
for (int i = 1; i <= n; i++)
if (cnt[a[i] + d])
to[i] = mp[a[i] + d];
int ans = 0;
for (int i = 1; i <= n; i++) {
if (f[i])
continue;
v.clear();
dp.clear();
dp.push_back(0);
int x = i;
while (x != 0)
v.push_back(cnt[a[x]]), f[x] = 1, x = to[x], dp.push_back(1e15);
for (int i = 0; i < (int)v.size(); i++) {
dp[i + 1] = min(dp[i] + v[i], dp[i + 1]);
if (i)
dp[i + 1] = min(dp[i - 1] + v[i], dp[i + 1]);
}
ans += min(dp[(int)v.size()], dp[(int)v.size() - 1]);
}
cout << ans << endl;
return 0;
}
有两个字符串多集合,分别是 X X X 和 Y Y Y ,它们最初都是空的。
给你 Q Q Q 个查询,让你按顺序处理。在第 i i i 个查询中,你会收到一个整数 T i T_i Ti 和一个字符串 S i S_i Si 。如果是 T i = 1 T_i=1 Ti=1 ,将 S i S_i Si 插入 X X X ;如果是 T i = 2 T_i=2 Ti=2 ,将 S i S_i Si 插入 Y Y Y 。
处理完每个查询后,打印此值:
前面的东西都是比较简单的算法,但是这道题需要前置知识字典树。
看到前缀,你想到了什么?没错就是字典树。
在每一个点上面都记录一下 Y Y Y 里面的字符串经过了这个点多少次。
然后还需要记录一下这个点是不是在某一个 X X X 串的末尾。
即可。
#include
using namespace std;
const int N = 500010;
int nxt[N][26];
vector<int> v[N];
bool f[N], f2[N];
int ans = 0;
int cnt = 1;
void add(string s, int id) {
int pos = 1;
bool x = 1;
for (auto i : s) {
if (!nxt[pos][i - 'a'])
nxt[pos][i - 'a'] = ++cnt;
pos = nxt[pos][i - 'a'];
if (f2[pos] == 1)
x = 0;
v[pos].push_back(id);
}
f[id] = x, ans += x;
}
void get(string s) {
int pos = 1;
bool ff = 0;
for (auto i : s) {
if (!nxt[pos][i - 'a'])
nxt[pos][i - 'a'] = ++cnt, ff = 1;
pos = nxt[pos][i - 'a'];
}
f2[pos] = 1;
if (!ff) {
for (auto i : v[pos])
if (f[i])
ans--, f[i] = 0;
v[pos].clear();
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int q;
cin >> q;
int nw = 0;
while (q--) {
int op;
cin >> op;
string s;
cin >> s;
if (op == 2)
add(s, ++nw);
else
get(s);
cout << ans << endl;
}
return 0;
}
有一个初始为空的序列 A A A 。
给你 Q Q Q 个查询,让你按顺序处理。下面解释一下 i i i -th 查询:
您将得到一个整数 y i y_i yi 。如果是 i = 1 i=1 i=1 ,让 z z z 成为 0 0 0 ;否则,让 z z z 成为 ( i − 1 ) (i-1) (i−1) \th查询的答案。定义 x i = ( ( y i + z ) m o d 1 0 9 ) + 1 x_i=((y_i+z)\bmod 10^9)+1 xi=((yi+z)mod109)+1 。将 x i x_i xi 追加到 A A A 的末尾。
然后,设 B = ( B 1 , B 2 , … , B i ) B=(B_1,B_2,\ldots,B_i) B=(B1,B2,…,Bi) 是按升序排序的序列 A A A ,求 B B B 中奇数索引元素的和。即求出 B 1 + B 3 + B 5 + ⋯ + B m B_1 + B_3 + B_5 + \dots + B_m B1+B3+B5+⋯+Bm ,其中 m m m 是不超过 i i i 的最大奇数。
动态开点权值线段树板子。
这道题和第一题有一点异曲同工之妙。
看到这种题,果断想到使用线段树来维护区间的奇数位和。因为值域有亿点点大,所以考虑动态开点权值线段树。
显然合并的话还是需要维护区间的偶数位和,然后再记录一下每一个区间里面出现了多少个数即可。
#include
#define mid ((l + r) >> 1)
#define int long long
using namespace std;
int q;
const int N = 3e7+10, mod = 1e9;
int ls[N], rs[N];
int cnt = 1;
struct node {
int sum1, sum2, len; //奇数位和,偶数位和,长度
} seg[N];
node merge(node x, node y) {
if (x.len == x.sum1 && x.len == x.sum2 && x.len == -1)
return y;
if (y.len == y.sum1 && y.len == y.sum2 && y.len == -1)
return x;
node ans;
ans.len = x.len + y.len;
if (x.len % 2 == 0)
ans.sum1 = x.sum1 + y.sum1, ans.sum2 = x.sum2 + y.sum2;
else
ans.sum1 = x.sum1 + y.sum2, ans.sum2 = x.sum2 + y.sum1;
return ans;
}
void upd(int u, int l, int r, int pos) {
if (l == r) {
if (seg[u].len % 2 == 0)
seg[u].len++, seg[u].sum1 += pos;
else
seg[u].len++, seg[u].sum2 += pos;
return ;
}
if (pos <= mid) {
if (ls[u] == 0)
ls[u] = ++cnt;
upd(ls[u], l, mid, pos);
} else {
if (rs[u] == 0)
rs[u] = ++cnt;
upd(rs[u], mid + 1, r, pos);
}
seg[u] = merge(seg[ls[u]], seg[rs[u]]);
}
signed main() {
seg[0] = {-1, -1, -1};
cin >> q;
int lst = 0;
while (q--) {
int x;
cin >> x;
x = (x + lst) % mod + 1;
upd(1, 1, 1e9, x);
cout << seg[1].sum1 << endl;
lst = seg[1].sum1;
}
return 0;
}