Codeforces Round 918 (Div. 4) : link
Problem A
略
Problem B
略
Problem C
判断总和是否为平方数。
Problem D: link
定义一个语言只由a,b,c,d,e构成。其中a,e为Vowel,其余为consonants。给定字符串s,判断s是否由CV或CVC形式的字符组成,还需对字符串分割。如ba属于CV形式,bab属于CVC形式。
思路: 我们可以定义vis数组,初始化为-1,vis[i] 不为-1时,表示字符i位置之前是可分割的,其值vis[i] 为转移过来的上一个位置。记录上一个路径就可以从结果反推出原来的分割情况。
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const int mod = 998244353;
bool isVowel(char s){
if(s == 'a' || s == 'e')
return true;
return false;
}
bool isConsonant(char s){
if(s == 'a' || s == 'e')
return false;
return true;
}
void solve() {
int n;
string s;
cin >> n >> s;
vector<int> vis(n + 1, -1);
vis[0] = 0;
for(int i = 0; i < n; i++){
char a, b, c;
a = s[i];
if(i + 1 < n){
b = s[i + 1];
if(isConsonant(a) && isVowel(b) && vis[i] != -1){
vis[i + 2] = i;
}
}
if(i + 2 < n){
b = s[i + 1];
c = s[i + 2];
if(isConsonant(a) && isVowel(b) && isConsonant(c) && vis[i] != -1){
vis[i + 3] = i;
}
}
}
// for(int i = 0; i <= n; i++){
// cout << vis[i] << (i == n ? '\n' : ' ');
// }
set<int> path;
int cur = n;
while(cur){
path.insert(cur);
cur = vis[cur];
}
for(int i = 0; i < n; i++){
if(i != 0 && path.find(i) != path.end()){
cout << ".";
}
cout << s[i];
}
cout << "\n";
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
Problem E:link
对于长度为n的序列a.判断是否存在这样的一个区间[L,R],使得奇数位置的和等于偶数位置的和。(n<=2e5)
思路: 二分并不能满足题意,区间长度与是否满足奇偶数位置和无关。考虑前缀和,奇数位置做加法,偶数位置做减法。若能找到相同前缀和,则说明存在这样的一个区间。
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const int mod = 998244353;
void solve() {
int n;
cin >> n;
vector<ll> a(n + 1);
vector<ll> pre(n + 1);
for(int i = 1; i <= n; i++){
cin >> a[i];
}
multiset<ll> s;
for(int i = 0; i <= n; i++){
pre[i] = (i == 0 ? 0 : pre[i - 1] + a[i] * (i % 2 == 1 ? 1 : -1));
s.insert(pre[i]);
}
for(int i = 0; i <= n; i++){
s.erase(s.find(pre[i]));
if(s.find(pre[i]) != s.end()){
cout << "YES\n";
return ;
}
}
cout << "NO\n";
return ;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
Problem F:link
n个人,第i个人从
a
i
a_i
ai 走到
b
i
b_i
bi,题目保证
a
i
<
b
i
,
且
a
i
,
b
i
d
i
s
t
i
n
c
t
a_i < b_i, 且a_i, b_i \: distinct
ai<bi,且ai,bidistinct。每个人的行进速度一致,走到
b
i
b_i
bi后会停下。而当两个人走到同一个位置时会greeting一次。求greeting的总数。(n <= 2e5, a,b < 1e9)
思路:考虑如何达成greeting,两个人
i
,
j
i, j
i,j。当
a
i
<
a
j
且
b
i
>
b
j
a_i < a_j 且 b_i > b_j
ai<aj且bi>bj 时, j会在
b
j
b_j
bj停下,i后边会经过一次
b
j
b_j
bj,达成一次greeting。注意greeting是相互的,两个人只计算一次即可。
非常朴素的想法是,先对a进行排序,这样后续的人都是在
a
i
a_i
ai之后的位置出发的。那么对于第i个人来说,要寻找后续
b
j
<
b
i
b_j < b_i
bj<bi 的人的个数。这里的计数可以通过树状数组来计算,先将所有的b都计入,轮到
i
i
i的时候删掉
b
i
b_i
bi的影响,再查询
[
a
i
,
b
i
]
[a_i, b_i]
[ai,bi]的区间和。这样就满足了上述的两个条件。当然,
a
i
,
b
i
a_i, b_i
ai,bi的取值为1e9,做之前需要先离散化。
注意的是,树状数组不要在函数内部初始化,不然每个testcase都开辟一次空间很费时间,会直接TLE。
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const int mod = 998244353;
int find(int x, vector<int> &val){
return lower_bound(val.begin(), val.end(), x) - val.begin() + 1;
}
struct Fenwick{
const int n;
vector<ll> a;
Fenwick(int n): n(n + 10), a(n + 10, 0) {}
void add(int x, ll v){
for(int i = x; i <= n; i += i & -i){
a[i] += v;
}
}
ll sum(int x){
ll res = 0;
while (x)
{
res += a[x];
x -= x & -x;
}
return res;
}
ll rangeSum(int l, int r){
return sum(r) - sum(l - 1);
}
void clear(){
for(int i = 0; i < n; i++){
a[i] = 0;
}
}
};
Fenwick BIT = Fenwick(maxn << 2);
void solve() {
int n;
cin >> n;
vector<pair<int,int>> p(n);
vector<int> val;
for(int i = 0; i < n; i++){
cin >> p[i].first >> p[i].second;
val.push_back(p[i].first), val.push_back(p[i].second);
}
sort(val.begin(), val.end());
val.erase(unique(val.begin(), val.end()), val.end());
for(int i = 0; i < n; i++){
p[i].first = find(p[i].first, val);
p[i].second = find(p[i].second, val);
}
sort(p.begin(), p.end(), [&](const pair<int,int> &a, const pair<int,int> &b){
if(a.first == b.first)
return a.second < b.second;
return a.first < b.first;
});
BIT.clear();
for(int i = 0; i < n; i++){
BIT.add(p[i].second, 1);
}
ll ans = 0;
for(int i = 0; i < n; i++){
BIT.add(p[i].second, -1);
ll val = BIT.rangeSum(p[i].first, p[i].second);
ans += val;
}
cout << ans << '\n';
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
Problem G:link
无向图有n个顶点,m条边,边权为w。Slavic从顶点1出发走到顶点n,在每个顶点可以获得一个自行车bike =
s
i
s_i
si,Slavic可以选择已获得的bike中的一个
s
k
s_k
sk,从
i
到
j
i到j
i到j的代价为
w
i
,
j
∗
s
k
w_{i,j} * s_k
wi,j∗sk。求走到顶点n的最小代价。(n <1e3, m<1e3)
思路:以第一个test case为例。S = {5,2,1,3,3}
最优的结果为19,路线为1->2->3->2->4->5. 可以看到为了省力,在到达2后又去3得到了s=1,这之后都使用s=1的bike即最小代价。
我们可以定义下状态 dp[u][curs],表示走到顶点u,且当前持有最小的bike为curs时的代价。这个状态通过一条边w,转移到dp[v][new_s]。
d
p
[
v
]
[
n
e
w
s
]
=
d
p
[
u
]
[
c
u
r
s
]
+
w
[
u
]
[
v
]
∗
c
u
r
s
dp[v][new_s] = dp[u][curs] + w[u][v] * curs
dp[v][news]=dp[u][curs]+w[u][v]∗curs
BFS更新dp状态即可,初始状态为dp[1][s[1]]。注意题目有重边。
code:
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 10;
const int mod = 998244353;
void solve() {
int n, m;
cin >> n >> m;
// 有重边
vector<pair<ll,ll>> vec[maxn];
vector<vector<ll>> graph = vector<vector<ll>>(n + 1, vector<ll>(n + 1, 1e16));
vector<ll> s(n + 1);
for(int i = 0, u, v, w; i < m; i++){
cin >> u >> v >> w;
graph[u][v] = min(graph[u][v], 1ll * w);
graph[v][u] = graph[u][v];
}
for(int i = 1; i <= n; i++){
cin >> s[i];
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(graph[i][j] != 1e16){
vec[i].push_back({j, graph[i][j]});
vec[j].push_back({i, graph[i][j]});
}
}
}
pair<ll,ll> st = make_pair(1ll, s[1]);
queue< pair<ll,ll> > q;
q.push(st);
vector<vector<ll>> dp(n + 1, vector<ll>(1e3 + 10, 1e12));
dp[1][s[1]] = 0;
while(q.size()){
auto p = q.front();
q.pop();
if(p.first == n){
continue;
}
ll u = p.first, curs = p.second;
for(auto U : vec[u]){
auto v = U.first, w = U.second;
ll temps = min(curs, s[v]);
if(1ll * dp[u][curs] + 1ll * graph[u][v] * curs < dp[v][temps]){
dp[v][temps] = dp[u][curs] + graph[u][v] * curs;
q.push( {v, temps} );
}
}
}
ll ans = *min_element(dp[n].begin(), dp[n].end());
// for(int i = 1; i <= n; i++){
// for(int j = 1; j <= 5; j++){
// cout << "i: " << i << " j: " << j << " dp[i][j]: " << dp[i][j] << '\n';
// }
// }
cout << ans << '\n';
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}