题意:给定一个字符串,找到唯一存在的大写字母的位置
S是一个长度在2和100之间的字符串,由大写和小写英文字母组成。
S正好有一个大写字母。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
using namespace std;
using ll = long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int dir[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };
const int N = 2e4 + 5;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
string s;
cin >> s;
int cnt = 1;
for (char c : s) {
if (isupper(c)) {
cout << cnt;
break;
}
++cnt;
}
return 0;
}
题意:给你一个长度为 5 N 5N 5N的数组,剔除 N N N个最小值, N N N个最大值,求剩下的数的平均值
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
using namespace std;
using ll = long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int dir[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };
const int N = 2e4 + 5;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
int cnt = n;
n *= 5;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
sort(a.begin(), a.end());
double ans = 0;
for (int i = cnt; i < n - cnt; i++) {
ans += a[i];
}
printf("%.8f", ans / (n - 2 * cnt));
return 0;
}
题意:你在二维平面的起点 ( 0 , 0 ) (0,0) (0,0)上,每次可以上下左右移动,给你一个字符串 S S S表示移动序列
R
;L
;U
;D
,你需要判断该移动序列有没有经过重复点的情况
1 ≤ N ≤ 2 × 1 0 5 1≤N≤2×10^5 1≤N≤2×105
思路: 考虑数据范围小,用set模拟即可
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
using namespace std;
using ll = long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int dir[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };
const int N = 2e4 + 5;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
set<pii> s;
int n;
cin >> n;
pii st = { 0, 0 };
s.insert(st);
bool flag = 0;
for (int i = 0; i < n; i++) {
char c;
cin >> c;
if (c == 'R') st = { st.first + 1, st.second };
if (c == 'L') st = { st.first - 1, st.second };
if (c == 'U') st = { st.first, st.second + 1};
if (c == 'D') st = { st.first, st.second - 1};
if (s.count(st)) flag = 1;
s.insert(st);
}
if (flag) cout << "Yes";
else cout << "No";
return 0;
}
题意:给你 n n n 张牌,每张牌的正面为 A [ i ] A[i] A[i] ,反面为 B [ i ] B[i] B[i] ,初始时所有牌都在正面,你可以翻转0或任意多张牌,使得对于每一对相邻的牌,写在其正面的整数是不同的,统计该方案数并mod 998244353
思路: 简单的递推dp,令 d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1]表示前 i i i 张牌中相邻的牌均不相同的方案数,且第 i i i 张牌为 A [ i ] / B [ i ] A[i]/B[i] A[i]/B[i],每次与前面一张不相同的牌转移即可
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
using namespace std;
using ll = long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int dir[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };
const int N = 2e5 + 5;
ll a[N][2], dp[N][2];
int mod = 998244353;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
dp[0][0] = 1;
for (int i = 1; i <= n; i++) cin >> a[i][0] >> a[i][1];
for (int i = 1; i <= n; i++) {
if (a[i][0] != a[i - 1][0])
dp[i][0] = (dp[i][0] + dp[i - 1][0]) % mod;
if (a[i][0] != a[i - 1][1])
dp[i][0] = (dp[i][0] + dp[i - 1][1]) % mod;
if (a[i][1] != a[i - 1][0])
dp[i][1] = (dp[i][1] + dp[i - 1][0]) % mod;
if (a[i][1] != a[i - 1][1])
dp[i][1] = (dp[i][1] + dp[i - 1][1]) % mod;
}
cout << (dp[n][0] + dp[n][1]) % mod;
return 0;
}
题意:给定一个长度为 n n n 的排列 A A A,给你 m m m 对关系 ( X i , Y i ) (X_i,Y_i) (Xi,Yi) 表示 A X i < A Y i A_{X_i}
Sample Input 1
3 2
3 1
2 3
Sample Output 1
Yes
3 1 2
We can uniquely determine that A=(3,1,2).
Sample Input 2
3 2
3 1
3 2
Sample Output 2
No
Two sequences (2,3,1) and (3,2,1) can be A.
Sample Input 3
4 6
1 2
1 2
2 3
2 3
3 4
3 4
Sample Output 3
Yes
1 2 3 4
思路: 拓扑排序即可,如果存在多个入度为0的点或者拓扑排序发现环,则无法确定该排列
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
using namespace std;
using ll = long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int dir[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };
const int maxn = 2e5 + 5;
int n, m;
vector<int> e[maxn];
int degree[maxn];
int a[maxn];
stack<int> s;
bool topo() {
int sz = 0;
bool finished = true;
for (int i = 1; i <= n; i++) {
if (!degree[i]) s.push(i);
}
while (!s.empty()) {
if (s.size() > 1) finished = false;
int k = s.top();
a[sz++] = k;
s.pop();
for (int i = 0; i < e[k].size(); i++)
if (--degree[e[k][i]] == 0)
s.push(e[k][i]);
}
if (sz < n || !finished) return false;
return true;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
while (m--) {
int u, v;
cin >> u >> v;
e[u].push_back(v);
degree[v]++;
}
if (!topo()) {
cout << "No" << "\n";
}
else {
cout << "Yes" << "\n";
vector<int> ans(n + 1);
int cnt = 1;
for (int i = 0; i < n; i++) {
ans[a[i]] = cnt++;
}
for (int i = 1; i <= n; i++) cout << ans[i] << " ";
}
return 0;
}
题意:有 N N N 个城市,给定一个 N × M N\times M N×M的矩阵 S S S,若 S [ i ] [ j ] = 1 S[i][j]=1 S[i][j]=1则表示城市 i i i 与 i + j i+j i+j 之间有一条有向边,边权均为 1,对于 k ∈ [ 2 , N ] k\in[2,N] k∈[2,N],你需要回答从城市1到城市 N N N且不经过城市 k k k的最短路,若不能到达则输出 − 1 -1 −1
Sample Input 1
5 2
11
01
11
10
00
Sample Output 1
2 3 2
Sample Input 2
6 3
101
001
101
000
100
000
Sample Output 2
-1 3 3 -1
思路: 本题有两个特殊之处:路径的各结点编号是递增的, M M M范围比较小。我们先正反向建图,跑两遍 dijkstra \text{dijkstra} dijkstra预处理出从起点出发到各结点的最短路 d 1 [ i ] d1[i] d1[i],以及从终点出发到各结点的最短路 d 2 [ i ] d2[i] d2[i]。对于每次需要去除的顶点 k k k,我们需要枚举每一条边 ( i , j ) (i,j) (i,j)满足 i ∈ [ 1 , k − 1 ] , j ∈ [ k + 1 , N ] i\in[1,k-1],j\in[k+1,N] i∈[1,k−1],j∈[k+1,N],则不经过顶点 k k k 的最短路即为
a n s = m i n i ∈ [ 1 , k − 1 ] j ∈ [ k + 1 , N ] S [ i ] [ j ] = 1 { d 1 [ i ] + d 2 [ j ] + 1 } ans=\mathop{min}\limits_{\substack{i\in[1,k-1]\\j\in[k+1,N]\\S[i][j]=1}}\{d1[i]+d2[j]+1\} ans=i∈[1,k−1]j∈[k+1,N]S[i][j]=1min{d1[i]+d2[j]+1}
由于 j − i ≤ M , 1 ≤ M ≤ 10 j-i\le M,1\le M\le 10 j−i≤M,1≤M≤10的特性,我们暴力枚举矩阵的 [ k − m + 1 , k − 1 ] [k-m+1, k-1] [k−m+1,k−1]行即可
时间复杂度为: O ( N M 2 + E log N ) O(NM^2+E\log N) O(NM2+ElogN), E E E为图的边数
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
using namespace std;
using ll = long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int dir[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };
const int maxn = 1e6 + 5;
ll inf = (1ll << 50);
int n, m, s, t;
vector<vector<pii>> e1, e2;
vector<ll> d1, d2;
void dijkstra(int s, vector<vector<pii>>& e, vector<ll>& d) {
vector<bool> vis(n + 1);
priority_queue<pll> q;
q.push({ d[s] = 0, s });
while (!q.empty()) {
pll now = q.top(); q.pop();
int u = now.second;
if (vis[u]) continue;
vis[u] = 1;
for (auto[to, w] : e[u]) {
if (d[to] > d[u] + w) {
d[to] = d[u] + w;
q.push({ -d[to], to });
}
}
}
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
s = 1, t = n;
e1.resize(n + 1), e2.resize(n + 1), d1.resize(n + 1, inf), d2.resize(n + 1, inf);
vector<string> g(n + 1);
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
s = " " + s;
g[i] = s;
for (int j = 1; j < s.size(); j++) {
if (s[j] == '1') {
e1[i].push_back({ i + j, 1 });
e2[i + j].push_back({ i, 1 });
}
}
}
dijkstra(s, e1, d1);
dijkstra(t, e2, d2);
for (int i = 2; i <= n - 1; i++) {
ll ans = inf;
for (int j = i - 1; j >= 1 && j > i - m; j--) {
for (int k = 1; k <= m; k++) {
if (j + k > i && g[j][k] == '1')
ans = min(ans, d1[j] + d2[j + k] + 1);
}
}
if (ans == inf) cout << -1 << " ";
else cout << ans << " ";
}
return 0;
}
F题有一个加强版
https://ac.nowcoder.com/acm/problem/14293?&headNav=acm
给定一张n个点,m条边的带权有向无环图,同时给定起点S和终点T,一共有q个询问,每次询问删掉某个点和所有与它相连的边之后S到T的最短路,询问之间互相独立(即删除操作在询问结束之后会立即撤销),如果删了那个点后不存在S到T的最短路,则输出-1。
第一行四个正整数表示n,m,S,T,意义如题所述;
接下来m行每行三个正整数x[i],y[i],z[i],表示有一条x[i]到y[i]的有向边,权值为z[i];
第m+1行一个正整数q表示询问次数;
接下来q行每行一个正整数a[i]表示这次询问要删除点a[i]。
n , q < = 1 0 5 n,q <= 10^5 n,q<=105
m < = 2 ∗ 1 0 5 m <= 2*10^5 m<=2∗105
z [ i ] < = 1 0 9 z[i] <= 10^9 z[i]<=109
q行每行一个数输出答案,如果删了这个点后不存在S到T的最短路,输出-1
6 7 1 5
1 2 2
2 3 4
3 4 3
4 5 5
3 5 9
1 6 10
6 5 13
4
3
4
2
6
23
15
23
14
本题相较于上题,每一条边 ( i , j ) (i,j) (i,j)没有了 j − i ≤ M j-i\le M j−i≤M的限制,同时路径的结点编号也不一定递增,且每条边权重不一定为1,但大体思路相同,先正反向建图,跑两遍 dijkstra \text{dijkstra} dijkstra预处理出从起点出发到各结点的最短路 d 1 [ i ] d1[i] d1[i],以及从终点出发到各结点的最短路 d 2 [ i ] d2[i] d2[i]。考虑拓扑序,对于删除的顶点 k k k ,一定有一条边 ( i , j ) (i,j) (i,j)从 k k k 的左边连接到 k k k 的右边, 即满足 t o p [ i ] < t o p [ k ] , t o p [ j ] > t o p [ k ] top[i]
查询时有个地方需要注意,对于 d 1 [ i ] = i n f d1[i]=inf d1[i]=inf或者 d 2 [ i ] = i n f d2[i]=inf d2[i]=inf的点,删除后并不会对 s → t s\rightarrow t s→t的最短路产生影响,结果仍为 d 1 [ t ] d1[t] d1[t],由于它们并不在 s → t s\rightarrow t s→t的路径上,所以使用拓扑序并不一定能更新到这些点
他们的拓扑序为:
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
id[i] | 1 | 4 | 2 | 5 | 6 | 7 | 3 | 8 |
如起点 s = 1 s=1 s=1,终点 t = 5 t=5 t=5,6和8的拓扑序比5的拓扑序更大,枚举边时不会被更新到,但删除他们的结果均为 1 → 2 → 5 = 6 1\rightarrow2\rightarrow5=6 1→2→5=6
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
using namespace std;
using ll = long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int dir[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };
const int maxn = 1e6 + 5;
ll inf = (1ll << 50);
int n, m, s, t;
vector<vector<pii>> e1, e2;
vector<ll> d1, d2;
int id[maxn], fid[maxn], degree[maxn], pos;
struct tag {
ll add;
tag operator + (const tag& t) const {
return { min(add, t.add) };
}
void clear() {
add = inf;
}
};
struct Node {
int l, r;
ll val;
tag t;
}tr[4 * maxn];
void pushup(int u) {
tr[u].val = min(tr[u << 1].val, tr[u << 1 | 1].val);
}
void settag(int u, tag& t) {
tr[u].t = tr[u].t + t;
tr[u].val = min(tr[u].val, tr[u].t.add);
}
void pushdown(int u) {
settag(u << 1, tr[u].t);
settag(u << 1 | 1, tr[u].t);
tr[u].t.clear();
}
void build(int u, int l, int r) {
tr[u] = { l, r, inf, {inf} };
if (l == r) return;
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void update(int u, int l, int r, tag t) {
if (tr[u].l >= l && tr[u].r <= r) {
settag(u, t);
return;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid) update(u << 1, l, r, t);
if (r > mid) update(u << 1 | 1, l, r, t);
pushup(u);
}
ll query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) {
// 特判不在s->t路径上的点
if (d1[fid[l]] == inf || d2[fid[l]] == inf) return d1[t];
else return tr[u].val;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (l > mid) return query(u << 1 | 1, l, r);
else if (r <= mid) return query(u << 1, l, r);
else return query(u << 1, l, r) + query(u << 1 | 1, l, r);
}
void dijkstra(int s, vector<vector<pii>>& e, vector<ll>& d) {
vector<bool> vis(n + 1);
priority_queue<pll> q;
q.push({ d[s] = 0, s });
while (!q.empty()) {
pll now = q.top(); q.pop();
int u = now.second;
if (vis[u]) continue;
vis[u] = 1;
for (auto [to, w] : e[u]) {
if (d[to] > d[u] + w) {
d[to] = d[u] + w;
q.push({ -d[to], to });
}
}
}
}
void topo() {
queue<int> q;
for (int i = 1; i <= n; i++) {
if (degree[i] == 0) {
q.push(i);
}
}
while (!q.empty()) {
int now = q.front(); q.pop();
id[now] = ++pos;
fid[pos] = now;
for (auto [to, w] : e1[now]) {
degree[to]--;
if (!degree[to]) {
q.push(to);
}
}
}
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m >> s >> t;
e1.resize(n + 1), e2.resize(n + 1), d1.resize(n + 1, inf), d2.resize(n + 1, inf);
while (m--) {
int u, v, w;
cin >> u >> v >> w;
degree[v]++;
e1[u].push_back({ v, w });
e2[v].push_back({ u, w });
}
dijkstra(s, e1, d1);
dijkstra(t, e2, d2);
topo();
build(1, 1, n);
for (int i = 1; i <= n; i++) {
for (auto [j, w] : e1[i]) {
if (id[i] != id[j] - 1 && d1[i] != inf && d2[j] != inf) {
update(1, id[i] + 1, id[j] - 1, { d1[i] + d2[j] + w });
}
}
}
int q;
cin >> q;
while (q--) {
int pos;
cin >> pos;
ll res = query(1, id[pos], id[pos]);
cout << (res == inf ? -1 : res) << "\n";
}
return 0;
}
/*
8 7 1 5
1 2 2
2 5 4
3 4 5
4 6 6
3 8 7
7 8 8
4 8 5
*/