题目的意思就是:从第一层开始往后起跳,每次可以跳到该层层数减一或者加一的层数,询问跳m次后最多可以经过多少对不重复的括号。
首先我们发现这个其实是一个树形结构。
那么我们很容易就能得到一个想法,如果钱不够的话,那一定是跳到树的最长链上。
但是如果钱够呢?那么我们就需要考虑来回走的情况,我们容易想到,最优的策略就是先在其他链上来回走,之后再到最长链上走不重复的路最优。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e7+10; char s[maxn*2]; int main() { int n, v; scanf("%d%d", &n, &v); scanf("%s", s+1); int cnt = 0, all = 0; int maxx = 0; for (int i = 1; i <= n; ++ i) { if (s[i] == '(') { cnt ++; ///最长链长度 all ++; ///总共可以到达的点 } else cnt --; if (cnt == 0) break; maxx = max(cnt, maxx); } int ans = 0; if (maxx >= v+1) { ///钱不够 ans = v+1; } else { if (v - (maxx-1) >= 2 * (all - maxx)) ///跟总数取小 ans = all; else { ans = maxx + (v - (maxx-1)) / 2; } } cout << ans << endl; return 0; }
题目其实就是一个最大匹配数的问题。不过这个匹配一定是从模式串的头部或者尾部开始。
所以我们能容易想到一个n2暴力的想法。
首先先确定长度较小的串是哪一个。之后按照以下的过程去模拟不断取大就行了。
就是先把大串前后填充长度为小串长度-1的空位,如果匹配到空位就continue。之后不匹配就break。
每次取大,之后大串小串长度相加减去最大匹配长度即为答案
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e6 + 10; int n, m; int main() { string a, b; cin >> a >> b; if (a.size() < b.size()) swap(a, b); int n = a.size(); int m = b.size(); for (int i = 0; i < b.size(); ++ i) { a = "0" + a; } for (int i = 0; i < b.size(); ++ i) { a = a + "0"; } int maxx = 0; for (int i = 0; i < (int)a.size() - (int)b.size(); ++ i) { int flag = 0, cnt = 0; for (int j = 0; j < b.size(); ++ j) { if (a[i+j] == '0') continue; if (a[i+j] == b[j] || a[i+j] == '?' || b[j] == '?') { cnt++; } else { flag = 1; break; } } if (flag == 0) maxx = max(maxx, cnt); } cout << n+m-maxx <<endl; return 0; }
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e6 + 10; const int mod = 99824353; int n, m; ll f[maxn]; ll qpow(ll a,ll b){ ll ans = 1,base = a; while(b){ if(b&1) ans = ans * base % mod; base = base * base % mod; b>>=1; } return ans; } void init(){ f[0]=1; for(int i=1;i<=2e5;i++){ f[i]=f[i-1]*i%mod; } } ll inv(ll n) { return qpow(n, mod-2); } int main() { ll n; cin >> n; n--; ll ans = qpow(2, n); ans = ans * n % mod; ans = ans * (n+1) % mod; ans = ans * inv(2) % mod; ans = ans * inv(2) % mod; //ans = cout << ans << endl; return 0; }
简单计数dp。所需要注意的是如果直接按题目给出的范围开数组的话是会爆空间的,所以我们需要先将其 /50之后就是几个状态的转移。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 200 + 5; const int mod = 1000000007; int n; ll dp[maxn][maxn][maxn]; void add(ll &x, ll y) { x = (1ll*x + y) % mod; } int main() { int a, b, c; scanf("%d%d%d", &a, &b, &c); a /= 50; b /= 50; c /= 50; for (int i = 0; i <= 200; ++ i) { for (int j = 0; j <= 200; ++ j) { for (int k = 0; k <= 200; ++ k) { if (i < 2*3 && j < 2*3 && k < 2*3) { dp[i][j][k] = 1; continue; } if (i >= 2*3) { add(dp[i][j][k], dp[i-2*3][j][k]); } if (i >= 3*3) { add(dp[i][j][k], dp[i-3*3][j][k]); } if (i >= 5*3) { add(dp[i][j][k], dp[i-5*3][j][k]); } if (j >= 2*3) { add(dp[i][j][k], dp[i][j-2*3][k]); } if (j >= 3*3) { add(dp[i][j][k], dp[i][j-3*3][k]); } if (j >= 5*3) { add(dp[i][j][k], dp[i][j-5*3][k]); } if (k >= 2*3) { add(dp[i][j][k], dp[i][j][k-2*3]); } if (k >= 3*3) { add(dp[i][j][k], dp[i][j][k-3*3]); } if (k >= 5*3) { add(dp[i][j][k], dp[i][j][k-5*3]); } } } } cout << dp[a][b][c] << endl; return 0; }
我们观察题目发现。胜负关系是满足拓扑序的,那么题意就转化为问入度为0的点有哪些。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e6 + 10; int n; int fa[maxn], du[maxn]; int _find(int x) { if (x == fa[x]) return x; return fa[x] = _find(fa[x]); } int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++ i) { fa[i] = i; du[i] = 0; } for (int i = 1; i <= m; ++ i) { int u, v; scanf("%d%d", &u, &v); du[v] ++; } int cnt = 0; for (int i = 1; i <= n; ++ i) { if (du[i] == 0) cnt++; } printf("%d ", cnt); return 0; }
我们会发现不管一行一列有多少个炮。最后都会因为开不了炮而被留下,那么只有当每行每列的炮的个数小于3的时候才会开不了炮,所以大于等于2的炮最后都会变成2
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e6 + 10; int n; int a[maxn]; int main() { int T; scanf("%d", &T); while (T--) { ll n, m; scanf("%lld%lld", &n, &m); if (n >= 2) n = 2; if (m >= 2) m = 2; cout << n*m << endl; } return 0; }
按照题意模拟即可,我们发现其实是一个按照拓扑序模拟的过程。
判断在拓扑过程中是否满足条件即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 5e4+10; int n, a[maxn], b[maxn], c[maxn], d[maxn], du[maxn]; vector<int> G[maxn]; int vis[maxn]; int main() { int n, status; scanf("%d%d", &n, &status); for (int i = 1; i <= n; ++ i) { scanf("%d%d", &a[i], &b[i]); } for (int i = 1; i <= n; ++ i) { scanf("%d%d", &c[i], &d[i]); } for (int i = 1; i <= n; ++ i) { int num; scanf("%d", &num); for (int j = 1; j <= num; ++ j) { int v; scanf("%d", &v); G[i].push_back(v); du[v] ++; } } queue<int> q; q.push(1); for (int i = 2; i <= n; ++ i) { if (du[i] == 0) { cout << "No" << endl; return 0; } } int cnt = 0; while (q.size()) { int u = q.front(); q.pop(); if (++ cnt > maxn) { cout << "No" << endl; return 0; } if (status >= a[u] || vis[b[u]] == 1) { status += c[u]; vis[d[u]] = 1; for (int i = 0; i < G[u].size(); ++ i) { int v = G[u][i]; if (--du[v] == 0) { q.push(v); } } } else { q.push(u); continue; } } for (int i = 1; i <= n; ++ i) { if (du[i]) { cout << "No" << endl; return 0; } } cout << "Yes" << endl; return 0; }
“练习者在心中将所有值小于等于x的数字都加上x“ 这句话十分的关键,
假设一个最坏的情况,所有a[i] = 1, x = 1。那么操作之后所有数字就变成了2
之后我们让x = 2, 4, 8, 16... 知道1e9
我们发现我们最多会操作log1e9次。每次操作是O(n)。所以复杂度就是nlog。
又因为我们需要找到<= x的数字。需要log的复杂度
所以总复杂度就是nloglog。只需要一个优先队列就好啦
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5+10; typedef pair<int, int> pii; int main() { ios::sync_with_stdio(); cin.tie(0); cout.tie(0); int n, m; cin >> n >> m; priority_queue<pii, vector<pii>, greater<pii> > pq; for (int i = 1; i <= n; ++ i) { int temp; cin >> temp; pq.push({temp, i}); } for (int i = 1; i <= m; ++ i) { int x; cin >> x; vector<pii> rec; while (pq.size() && pq.top().first <= x) { pii cur = pq.top(); pq.pop(); cur.first += x; rec.push_back(cur); } for (auto i : rec) pq.push(i); } vector<int> ans(n+1); while (pq.size()) { pii cur = pq.top(); pq.pop(); ans[cur.second] = cur.first; } for (int i = 1; i <= n; ++ i) { if (i != 1) cout << " "; cout << ans[i]; } cout << endl; return 0; }
简单BFS不讲了..
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e6 + 10; int n, m; char G[1005][1005]; int dic[4][2] = {1,0, 0,1, -1,0, 0, -1}; int vis[1005][1005]; void BFS() { queue<pair<int, int> > q; for (int i = 1; i <= m; ++ i) { q.push({0, i}); q.push({n+1, i}); } for (int i = 1; i <= n; ++ i) { q.push({i, 0}); q.push({i, m+1}); } while (q.size()) { int x = q.front().first; int y = q.front().second; q.pop(); for (int i = 0; i < 4; ++ i) { int tx = x + dic[i][0]; int ty = y + dic[i][1]; if (tx < 1 || ty < 1 || tx > n || ty > m || G[tx][ty] == '1') continue; if (vis[tx][ty] ) continue; vis[tx][ty] = 1; q.push({tx, ty}); } } return ; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++ i) { scanf("%s", G[i]+1); } BFS(); int cnt = 0; for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= m; ++ j) { if (G[i][j] == '0' && vis[i][j]) cnt ++; } } cout << cnt << endl; return 0; }
字符串hash + 线段树维护。
自然溢出hash即可。
单点修改,区间查询。问题是如何check是否相似相等呢,我们发现相似相等只有一个位置不同,所以我们可以二分这个位置。
如果左边的hash值都相同说明mid可以往右靠,右边hash值都相同就说明mid可以往左靠。
最后只需要判断一下最后的位置是否是相似相等就好啦。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5+10; #define ls(x) (x << 1) #define rs(x) (x << 1 | 1) typedef unsigned long long ull; ull tree[maxn << 2]; char s[maxn]; ull base = 131, pw[maxn]; int n, m; void pushup(int rt, int rnum) { tree[rt] = tree[ls(rt)]* pw[rnum] + tree[rs(rt)] ; } void build(int rt, int l, int r) { if (l == r) { tree[rt] = (s[l]-'a'); return ; } int mid = l + r >> 1; build(ls(rt), l, mid); build(rs(rt), mid+1, r); pushup(rt, r-mid); } void update(int rt, int l, int r, int pos, char val) { if (l == r) { s[l] = val; tree[rt] = (s[l]-'a'); return ; } int mid = l + r >> 1; if (pos <= mid) update(ls(rt), l, mid, pos, val); if (pos > mid) update(rs(rt), mid+1, r, pos, val); pushup(rt, r-mid); } ull query(int rt, int l, int r, int ql, int qr) { if (ql <= l && r <= qr) { return tree[rt] * pw[qr - r]; } int mid = l + r >> 1; ull ans = 0; if (ql <= mid) ans += query(ls(rt), l, mid, ql, qr); if (qr > mid) ans += query(rs(rt), mid+1, r, ql, qr); return ans; } bool check(int l1, int r1, int l2, int r2) { int len = r1-l1+1; if (r2-l2+1 != len) return 0; int L = 0, R = len-1; int ans = -1; // if (query(1, 1, n, l2, r2) == query(1, 1, n, l1, r1)) { // return 1; // } while (L <= R) { int mid = L + R >> 1; ull vl1 = query(1, 1, n, l1, l1+mid); ull vl2 = query(1, 1, n, l2, l2+mid); ull vr1 = query(1, 1, n, l1+mid, r1); ull vr2 = query(1, 1, n, l2+mid, r2); if (vl1 == vl2) { L = mid + 1; ans = max(ans, mid); } else { R = mid - 1; } } //cout << ans << endl; return (ans == -1 && query(1, 1, n, l1+1, r1) == query(1, 1, n, l2+1, r2)) || ans >= len-2 || query(1, 1, n, l1+ans+2, r1) == query(1, 1, n, l2+ans+2, r2); } int main() { scanf("%d%d", &n, &m); scanf("%s", s+1); pw[0] = 1; for (int i = 1; i <= n; ++ i) { pw[i] = pw[i-1] * base; } build(1, 1, n); for (int i = 1; i <= m; ++ i) { int opt; scanf("%d", &opt); if (opt == 1) { int pos; char ss[2]; scanf("%d%s", &pos, ss); update(1, 1, n, pos, ss[0]); } else { int l1, r1, l2, r2; scanf("%d%d%d%d", &l1, &r1, &l2, &r2); if (check(l1, r1, l2, r2)) { printf("YES "); } else { printf("NO "); } } } return 0; }
完结撒花~~