链接:https://ac.nowcoder.com/acm/contest/9984#question
目录
听完雨巨的分析后豁然开朗许多%%%,
A 九峰与签到题(签到)
注意一下任意时间的意思,即每个时间都要满足才行
#include <bits/stdc++.h>
using namespace std;
//#define ACM_LOCAL
#define fi first
#define se second
#define il inline
#define re register
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int N = 5e5 + 10;
const int M = 5e5 + 10;
const ll INF = 1e18;
const double eps = 1e-5;
const int MOD = 1e9;
int vis[N];
void solve() {
int n, m; cin >> n >> m;
map<int, int> mp1; map<int, int> mp2;
vector<int> ans;
for (int i = 1; i <= n; i++) {
int opt;
string s;
cin >> opt >> s;
mp1[opt]++;
if (s == "AC") mp2[opt]++;
if (mp1[opt] > 2 * mp2[opt]) {
vis[opt] = 1;
}
}
int f = 0;
for (int i = 1; i <= 20; i++) {
if (!vis[i] && mp1[i]) {
printf("%d ", i);
f = 1;
}
}
if (!f) printf("-1\n");
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
}
B 武辰延的字符串(二分+字符串哈希)
有两个串s和t,我们现在要两个s的前缀拼出t的前缀,最普通的方法就是O2 的暴力枚举,考虑怎么去减少枚举,那自然就是二分了。
先用字符串哈希O(N)处理好两个字符串,然后枚举第一个前缀,我们用二分找第二个前缀最多可以匹配到多少长度为止,因为确定第一个前缀后,方案数就是第二个前缀最长匹配长度。
然后不断枚举第一个前缀,直到失配退出,把结果相加就可以啦。
#include <bits/stdc++.h>
using namespace std;
//#define ACM_LOCAL
#define fi first
#define se second
#define il inline
#define re register
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int N = 2e5 + 10;
const int M = 5e5 + 10;
const ll INF = 1e18;
const double eps = 1e-5;
const int MOD = 1e9 + 7;
ull f1[N], f2[N], p[N];
char s1[N], s2[N];
int n, m;
ull Get(ull *f, int l, int r) {
return f[r] - f[l-1] * p[r - l + 1];
}
unordered_map<ull, int> mp;
void solve() {
scanf("%s", s1+1);
scanf("%s", s2+1);
n = strlen(s1+1); m = strlen(s2+1);
p[0] = 1;
for (int i = 1; i <= n; i++) {
f1[i] = f1[i-1] * 1331 + (s1[i] - 'a' + 1);
mp[f1[i]] = 1;
p[i] = p[i-1] * 1331;
}
for (int i = 1; i <= m; i++) {
f2[i] = f2[i-1] * 1331 + (s2[i] - 'a' + 1);
}
ll ans = 0;
for (int i = 1; i <= m; i++) {
if (s1[i] != s2[i]) break;
int l = i+1, r = m;
while (l <= r) {
int mid = (l + r) >> 1;
if (mp[Get(f2, i+1, mid)]) l = mid + 1;
else r = mid - 1;
}
ans += (r - i);
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
}
E 九峰与子序列(DP+字符串哈希 | 暴力)
有点像背包的题目,但多了一个判断能不能取的条件
写出状态:f[i][j]代表前i个字符串匹配到前j个位置有多少方案
列出转移方程:f[i][j] = f[i-1][j] + f[i-1][j-len] * (hash[j-len+1, j] == 串i)
但这样会mle,所以用滚动数组优化一下就好了
#include <bits/stdc++.h>
using namespace std;
//#define ACM_LOCAL
#define fi first
#define se second
#define il inline
#define re register
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int N = 5e6 + 10;
const int M = 5e5 + 10;
const ll INF = 1e18;
const double eps = 1e-5;
const int MOD = 1e9 + 7;
char s1[N], s2[N];
ull f[N], p[N];
ll dp[N];
ull Get(int l, int r) {
return f[r] - f[l-1] * p[r - l + 1];
}
void solve() {
int n, len; cin >> n >> s1+1;
len = strlen(s1+1);
p[0] = 1;
for (int i = 1; i <= len; i++) {
f[i] = f[i-1] * 1331 + (s1[i] - 'a' + 1);
p[i] = p[i-1] * 1331;
}
dp[0] = 1;
for (int i = 1; i <= n; i++) {
cin >> s2 + 1;
int l = strlen(s2+1);
ull tmp = 0;
for (int j = 1; j <= l; j++) tmp = tmp * 1331 + (s2[j] - 'a' + 1);
for (int j = len; j >= l; j--) {
dp[j] += dp[j-l] * (Get(j-l+1, j) == tmp);
}
}
cout << dp[len] << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
}
F 魏迟燕的自走棋(贪心+并查集)
和之前牛客多校的一道签到题很像,当时是用HK冲过去的,std应该是并查集。
这题也是用并查集维护,思维非常巧妙。
我们将每个人看成一个点,武器的能力值抽象成边,这样就转化成图论的模型了。
我们从联通块角度去看,如果一个联通块还没有形成环,那么他们之间的武器是可以互相换的,即他们当中的任意点都可以拥有新武器,我们称这个联通块没有饱和。反之,如果联通块内有环,那么这个块中的点都不能再拥有别的武器了我们称这个联通块饱和。
接下来我们分类讨论一下所有询问的情况:
- 两个点属于同一个联通块,如果这个联通块是饱和的,就略过;如果这个联通块不饱和,就加上当前武器的能力值并把联通块设置成饱和。
- 两个点属于不同的联通块,如果其中一个点是饱和的,那就将合并后的联通块设成饱和;如果都不是饱和,合并后也是不饱和;如果两个都是饱和的,就略过。
#include <bits/stdc++.h>
using namespace std;
//#define ACM_LOCAL
#define fi first
#define se second
#define il inline
#define re register
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int N = 2e5 + 10;
const int M = 5e5 + 10;
const ll INF = 1e18;
const double eps = 1e-5;
const int MOD = 1e9 + 7;
struct Query {
int x, y, w;
int id;
bool operator < (const Query& rhs) const {
return w > rhs.w;
}
}q[N];
int fa[N], flag[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
void solve() {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) fa[i] = i, flag[i] = 0;
for (int i = 1; i <= m; i++) {
int k; cin >> k;
if (k == 1) {
cin >> q[i].x >> q[i].w;
q[i].y = q[i].x;
} else {
cin >> q[i].x >> q[i].y >> q[i].w;
}
}
sort(q+1, q+m+1);
ll ans = 0;
for (int i = 1; i <= m; i++) {
int fx = find(q[i].x), fy = find(q[i].y);
if (fx == fy) {
if (!flag[fx]) {
ans += q[i].w;
flag[fx] = 1;
}
} else {
if (flag[fx] && flag[fy]) continue;
else if (!flag[fx] && !flag[fy]) {
ans += q[i].w;
fa[fx] = fy;
} else {
ans += q[i].w;
fa[fx] = fy;
flag[fy] = 1;
}
}
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
}
G 九峰与蛇形填数(线段树)
听说很多人都是暴力+剪枝过的,而且std还卡常 。
因为我们只要知道最后覆盖的是什么就可以,所以用二维的线段树去区间覆盖,N*Nlog(N)的复杂度。
最后我们可以通过起始位置和k, O(1)推出当前位置的值(分奇偶考虑一下,简单推一下式子),也不多说,具体看代码。
#include <bits/stdc++.h>
using namespace std;
//#define ACM_LOCAL
#define fi first
#define se second
#define il inline
#define re register
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int N = 2000 + 10;
const int M = 3000 + 10;
const ll INF = 1e18;
const double eps = 1e-5;
const int MOD = 1e9 + 7;
struct node {
struct tree {
int l, r;
int lazt, sum;
}t[N<<2];
void push_down(int u) {
if (t[u].lazt) {
t[u<<1].lazt = t[u<<1|1].lazt = t[u].lazt;
t[u<<1].sum = t[u<<1|1].sum = t[u].lazt;
t[u].lazt = 0;
}
}
void build(int u, int l, int r) {
t[u].l = l, t[u].r = r;
if (l == r) return;
int mid = (l + r) >> 1;
build(u<<1, l, mid);
build(u<<1|1, mid+1, r);
}
void modify(int u, int ql, int qr, int val) {
if (ql <= t[u].l && qr >= t[u].r) {
t[u].lazt = val;
t[u].sum = val;
return;
}
push_down(u);
int mid = (t[u].l + t[u].r) >> 1;
if (ql <= mid) modify(u<<1, ql, qr, val);
if (qr > mid) modify(u<<1|1, ql, qr, val);
}
int query(int u, int pos) {
if (t[u].l == t[u].r) return t[u].sum;
int mid = (t[u].l + t[u].r) >> 1;
push_down(u);
if (pos <= mid) return query(u<<1, pos);
else return query(u<<1|1, pos);
}
}tree[2005];
int a[M], b[M], k[M];
inline int bf(int x, int y, int ai, int bi, int ki) {
int tmp = x - ai + 1;
if (tmp % 2 == 1) return tmp * ki - (bi+ki-1-y);
else return (tmp-1)*ki+1 + (bi+ki-1-y);
}
void solve() {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; ++i) tree[i].build(1, 1, n);
for (int i = 1; i <= m; ++i) {
cin >> a[i] >> b[i] >> k[i];
for (int j = a[i]; j <= a[i]+k[i]-1; ++j) {
tree[j].modify(1, b[i], b[i]+k[i]-1, i);
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
int cnt = tree[i].query(1, j);
if (cnt == 0) printf("0 ");
else printf("%d ", bf(i, j, a[cnt], b[cnt], k[cnt]));
}
printf("\n");
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
}
H 吴楚月的表达式(模拟)
因为只有+ - * / 四种运算,所以这里可以用到一个小的trick。
我们把当前节点的值看成是 a + b
假设当前运算符为x,下一个节点值为a[v]
- x为 ‘+’ : a的值变成a+b,b的值变为a[v]
- x为 ‘-’ : a的值变成a+b,b的值变为-a[v]
- x为 '’ : a的值仍为a,b的值变为ba[v]
- x为 ‘/’ : a的值仍为a,b的值变为b/a[v]
#include <bits/stdc++.h>
using namespace std;
//#define ACM_LOCAL
#define fi first
#define se second
#define il inline
#define re register
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int N = 2e5 + 10;
const int M = 5e5 + 10;
const ll INF = 1e18;
const double eps = 1e-5;
const int MOD = 1e9 + 7;
int n, cnt, h[N], a[N], fa[N];
ll ans[N];
struct Edge {
int to, next;
char w;
}e[M];
void add(int u, int v, char w) {
e[cnt].to = v;
e[cnt].w = w;
e[cnt].next = h[u];
h[u] = cnt++;
}
ll ksm(ll a, ll b) {
ll res = 1, base = a;
while (b) {
if (b & 1) res = res * base % MOD;
base = base * base % MOD;
b >>= 1;
}
return res;
}
void dfs(int u, int far, ll pre, ll now) {
for (int i = h[u]; ~i; i = e[i].next) {
int v = e[i].to;
if (v == far) continue;
if (e[i].w == '+') dfs(v, u, (now+pre+MOD)%MOD, a[v]);
else if (e[i].w == '-') dfs(v, u, (now+pre+MOD)%MOD, -a[v]);
else if (e[i].w == '*') dfs(v, u, pre, now*a[v]%MOD);
else dfs(v, u, pre, now*ksm(a[v], MOD-2)%MOD);
}
ans[u] = (pre+now+MOD)%MOD;
}
void solve() {
cin >> n; memset(h, -1, sizeof h);
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 2; i <= n; i++) cin >> fa[i];
for (int i = 2; i <= n; i++) {
char c; cin >> c;
add(i, fa[i], c);
add(fa[i], i, c);
}
dfs(1, 0, 0, a[1]);
for (int i = 1; i <= n; i++) printf("%lld%c", ans[i], i == n ? '\n' : ' ');
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
}
J 邬澄瑶的公约数(质因数分解)
我们知道求最大公因数可以转化为公共质因子之积
把所有数质因数分解一下,然后求出最小的公共质因子之积
#include <bits/stdc++.h>
using namespace std;
//#define ACM_LOCAL
#define fi first
#define se second
#define il inline
#define re register
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int N = 1e4 + 10;
const int M = 5e5 + 10;
const ll INF = 1e18;
const double eps = 1e-5;
const int MOD = 1e9+7;
ll ksm(ll a, ll b) {
ll res = 1, base = a % MOD;
while (b) {
if (b & 1) res = res * base % MOD;
base = base * base % MOD;
b >>= 1;
}
return res % MOD;
}
int prime[N], k = 0;
bool is_prime[N];
void get_prime(){
memset(is_prime, true, sizeof is_prime);
is_prime[0] = is_prime[1] = false;
for(int i = 2 ; i < N;i++){
if (is_prime[i]) prime[++k] = i;
for(int j = 1; j <= k && i * prime[j] < N;j++){
is_prime[i * prime[j]] = false; //得到合数的最小质因子
if(i % prime[j] == 0) break;
}
}
}
ll a[N], b[N], Min[N];
void get_fac(int x, int j) {
for (int i = 1; i <= k; i++) {
//if (prime[i] * prime[i] > x) break;
int cnt = 0;
while (x % prime[i] == 0) x /= prime[i], cnt++;
Min[i] = min(Min[i], cnt * b[j]);
}
}
void solve() {
get_prime();
int n; cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = 1; i <= k; i ++) Min[i] = INF;
for (int i = 1; i <= n; i++) {
get_fac(a[i], i);
}
ll ans = 1;
for (int i = 1; i <= k; i++) {
if (Min[i] != INF)
ans = ans * ksm(prime[i], Min[i]) % MOD;
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
}