2024 停课做题总结
[ABC372D] Buildings
思路
正着做不方便,倒着用单调栈做一遍就行了。
代码
#include<iostream>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 2e5 + 10;
int n, ans[N];
int h[N], stk[N], top;
int main(){
n = read();
for (int i = 1; i <= n; i++) h[i] = read();
h[0] = 0x7fffffff;
for (int i = n; i >= 1; i--){
ans[i] = top;
while (h[stk[top]] < h[i]) top--;
stk[++top] = i;
}
for (int i = 1; i <= n; i++) cout << ans[i] << ' ';
return 0;
}
[ABC372E] K-th Largest Connected Components
思路
注意到,
代码
#include<iostream>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 2e5 + 10, M = 15;
int n, q;
int fa[N], e[N][M], a[N], len[N];
int find(int x){
return (x == fa[x] ? fa[x] : fa[x] = find(fa[x]));
}
void merge(int x, int y){
int fx = find(x), fy = find(y);
if (fx == fy) return;
fa[fx] = fy;
for (int i = 1, j = 1, k = 1; k <= min(10, len[fx] + len[fy]); k++){
if (e[fx][i] > e[fy][j]) a[k] = e[fx][i++];
else a[k] = e[fy][j++];
}
len[fy] = min(10, len[fx] + len[fy]);
for (int i = 1; i <= len[fy]; i++) e[fy][i] = a[i];
}
int main(){
n = read(), q = read();
for (int i = 1; i <= n; i++) fa[i] = i, e[i][1] = i, len[i] = 1;
for (int i = 1; i <= q; i++){
int opt = read(), u = read(), v = read();
if (opt == 1) merge(u, v);
else{
int x = find(u);
cout << (e[x][v] == 0 ? -1 : e[x][v]) << '\n';
}
}
return 0;
}
[ABC372F] Teleporting Takahashi 2
思路
好题!看完题目,可以很快想到一个
考虑怎么统计答案,很显然,有多余连接的点可以直接求,而没有多余连接的点就是它所在的有向边指向的点的
代码
#include<iostream>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 2e5 + 10, M = 55, K = 2e5 + 10, mod = 998244353;
int n, m, k, ans;
int x[M], y[M];
bool vis[N];
struct edge{
int v, w, nxt;
}e[N];
int head[N], cnt, id[N], tot;
void add(int u, int v, int w){
e[++cnt] = (edge){v, w, head[u]};
head[u] = cnt;
}
int dp[M << 2][K << 2];
signed main(){
n = read(), m = read(), k = read();
for (int i = 1; i <= m; i++) x[i] = read(), y[i] = read(), add((id[x[i]] == 0 ? id[x[i]] = ++tot : id[x[i]]), (id[y[i]] == 0 ? id[y[i]] = ++tot : id[y[i]]), 1);
for (int i = 1, last = 1; i <= n; i++){
bool ok = 0;
vis[i] = 1;
for (int j = 1; j <= m; j++) if (x[j] == i || y[j] == i) ok = 1;
if (ok && i != 1) add((id[last] == 0 ? id[last] = ++tot : id[last]), (id[i] == 0 ? id[i] = ++tot : id[i]), i - last), vis[i] = 0, last = i;
if (i == n){
if (last != 1) add((id[last] == 0 ? id[last] = ++tot : id[last]), (id[1] == 0 ? id[1] = ++tot : id[1]), n - last + 1);
else add((id[last] == 0 ? id[last] = ++tot : id[last]), 1, n);
}
}
vis[1] = 0;
dp[(id[1] == 0 ? id[1] = ++tot : id[1])][0] = 1;
for (int i = 0; i <= k; i++){
for (int u = 1; u <= tot; u++){
for (int j = head[u]; j; j = e[j].nxt){
int v = e[j].v, w = e[j].w;
if (i + w <= k) dp[v][i + w] = (dp[v][i + w] + dp[u][i]) % mod;
}
}
}
for (int i = 1, last = 1; i <= n; i++){
if (!vis[i]) ans = (ans + dp[(id[i] == 0 ? id[i] = ++tot : id[i])][k]) % mod, last = i;
else{
ans = (ans + dp[(id[last] == 0 ? id[last] = ++tot : id[last])][k - (i - last)]) % mod;
}
}
cout << ans;
return 0;
}
Invertible Bracket Sequences
思路
考虑一个合法的括号序列是怎样的,将 "(" 抽象成
枚举一个反转的左端点,看有多少个右端点能与它反转后符合题目条件。设左端点为
代码
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 2e5 + 10;
int T, n;
long long ans;
char s[N];
int a[N], pre[N], t[N << 2];
void build(int now, int l, int r){
if (l == r){
t[now] = pre[l];
return;
}
int mid = (l + r) >> 1;
build(now << 1, l, mid);
build(now << 1 | 1, mid + 1, r);
t[now] = max(t[now << 1], t[now << 1 | 1]);
}
int query(int now, int l, int r, int x, int y){
if (x <= l && r <= y) return t[now];
int mid = (l + r) >> 1, res = 0;
if (x <= mid) res = max(res, query(now << 1, l, mid, x, y));
if (mid + 1 <= y) res = max(res, query(now << 1 | 1, mid + 1, r, x, y));
return res;
}
vector<int> cnt[N];
signed main(){
T = read();
while (T--){
cin >> s + 1;
n = strlen(s + 1);
for (int i = 1; i <= n; i++) a[i] = (s[i] == '(' ? 1 : -1), pre[i] = pre[i - 1] + a[i];
build(1, 1, n);
for (int i = n; i >= 1; i--){
int l = 0, r = cnt[pre[i]].size() - 1, k = r + 1;
while (l <= r){
int mid = (l + r) >> 1, res = query(1, 1, n, i, cnt[pre[i]][mid]);
if (2 * pre[i] >= res){
r = mid - 1;
k = mid;
}else{
l = mid + 1;
}
}
ans += cnt[pre[i]].size() - k;
cnt[pre[i]].emplace_back(i);
}
cout << ans << '\n';
for (int i = 0; i <= n; i++) cnt[i].clear();
ans = 0;
}
return 0;
}
Beauty of the mountains
思路
首先,将整个有雪与无雪的差记录下来,记为
代码
#include<iostream>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 5e2 + 10;
int T, n, m, k, ans;
int a[N][N], b[N][N], s[N][N], sum;
int gcd(int a, int b){
return (b == 0 ? a : gcd(b, a % b));
}
signed main(){
T = read();
while (T--){
n = read(), m = read(), k = read();
sum = ans = 0;
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++) s[i][j] = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) a[i][j] = read();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++){
char c;cin >> c;
b[i][j] = (c == '1' ? 1 : -1);
sum += b[i][j] * a[i][j];
}
if (sum == 0){
cout << "YES\n";
continue;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + b[i][j];
bool f = 0;
for (int i = k; i <= n; i++)
for (int j = k; j <= m; j++){
int res = s[i][j] - s[i][j - k] - s[i - k][j] + s[i - k][j - k];
f = 1;
if (!f) ans = res;
ans = gcd(ans, res);
}
if (ans == 0){
cout <<"NO\n";
continue;
}
if (-sum % ans == 0) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
[ABC268E] Chinese Restaurant (Three-Star Version)
思路
由于沮丧值与菜的移动的次数有关,所以求出每个移动次数的贡献,而每个移动次数的贡献又由前一个移动次数的贡献转移而来。
所以,当一个菜移动时,会对一个人产生连续的沮丧值增加或减少,特别地,当
代码
#include<iostream>
#include<cmath>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 2e5 + 10;
int n, sum, ans = 0;
int a[N];
int get(int a, int b){
if (a < 0) return (a % b == 0 ? a / b : a / b - 1);
return a / b;
}
struct tree{
int sum, tag;
}t[N << 2];
void pushup(int now){
t[now].sum = t[now << 1].sum + t[now << 1 | 1].sum;
}
void pushdown(int now, int l, int r){
int mid = (l + r) >> 1;
t[now << 1].sum += t[now].tag * (mid - l + 1), t[now << 1 | 1].sum += t[now].tag * (r - mid);
t[now << 1].tag += t[now].tag, t[now << 1 | 1].tag += t[now].tag;
t[now].tag = 0;
}
void modify(int now, int l, int r, int x, int y, int k){
if (x <= l && r <= y){
t[now].sum += k * (r - l + 1);
t[now].tag += k;
return;
}
pushdown(now, l, r);
int mid = (l + r) >> 1;
if (x <= mid) modify(now << 1, l, mid, x, y, k);
if (mid + 1 <= y) modify(now << 1 | 1, mid + 1, r, x, y, k);
pushup(now);
}
int query(int now, int l, int r, int x, int y){
if (x <= l && r <= y){
return t[now].sum;
}
pushdown(now, l, r);
int mid = (l + r) >> 1, res = 0;
if (x <= mid) res += query(now << 1, l, mid, x, y);
if (mid + 1 <= y) res += query(now << 1 | 1, mid + 1, r, x, y);
return res;
}
signed main(){
n = read();
for (int i = 0; i < n; i++){
a[i] = read();
int x = min(i, a[i]), y = max(i, a[i]);
sum += min(y - x, x + n - y);
}
for (int i = 0; i < n; i++){
if (n & 1){
if (i <= a[i]){
int k = get(n + 2 * i - 2 * a[i], 2), g = get(3 * n + 2 * i - 2 * a[i], 2);
if (1 <= min(k, n)) modify(1, 1, n, 1, min(k, n), 1);
if (1 <= k + 1 && k + 1 <= n) modify(1, 1, n, k + 1, k + 1, 0);
if (max(k + 2, 1ll) <= min(n + i - a[i], n)) modify(1, 1, n, max(k + 2, 1ll), min(n + i - a[i], n), -1);
if (max(n + i - a[i] + 1, 1ll) <= min(g, n)) modify(1, 1, n, max(n + i - a[i] + 1, 1ll), min(g, n), 1);
if (1 <= g + 1 && g + 1 <= n) modify(1, 1, n, g + 1, g + 1, 0);
if (max(g + 2, 1ll) <= n) modify(1, 1, n, max(g + 2, 1ll), n, -1);
}else{
int k = n / 2, g = get(2 * i - n - 2 * a[i], 2);
if (1 <= min(g, n)) modify(1, 1, n, 1, min(g, n), 1);
if (1 <= g + 1 && g + 1 <= n) modify(1, 1, n, g + 1, g + 1, 0);
if (max(g + 2, 1ll) <= min(i - a[i], n)) modify(1, 1, n, max(g + 2, 1ll), min(i - a[i], n), -1);
if (max(i - a[i] + 1, 1ll) <= min(i - a[i] + k, n)) modify(1, 1, n, max(i - a[i] + 1, 1ll), min(i - a[i] + k, n), 1);
if (1 <= i - a[i] + k + 1 && i - a[i] + k + 1 <= n) modify(1, 1, n, i - a[i] + k + 1, i - a[i] + k + 1, 0);
if (max(i - a[i] + k + 2, 1ll) <= n) modify(1, 1, n, max(i - a[i] + k + 2, 1ll), n, -1);
}
}else{
if (i <= a[i]){
int k = get(n + 2 * i - 2 * a[i], 2), g = get(3 * n + 2 * i - 2 * a[i], 2);
if (1 <= min(k, n)) modify(1, 1, n, 1, min(k, n), 1);
if (max(k + 1, 1ll) <= min(n + i - a[i], n)) modify(1, 1, n, max(k + 1, 1ll), min(n + i - a[i], n), -1);
if (max(n + i - a[i] + 1, 1ll) <= min(g, n)) modify(1, 1, n, max(n + i - a[i] + 1, 1ll), min(g, n), 1);
if (max(g + 1, 1ll) <= n) modify(1, 1, n, max(g + 1, 1ll), n, -1);
}else{
int k = n / 2, g = get(2 * i - n - 2 * a[i], 2);
if (1 <= min(g, n)) modify(1, 1, n, 1, min(g, n), 1);
if (max(g + 1, 1ll) <= min(i - a[i], n)) modify(1, 1, n, max(g + 1, 1ll), min(i - a[i], n), -1);
if (max(i - a[i] + 1, 1ll) <= min(i - a[i] + k, n)) modify(1, 1, n, max(i - a[i] + 1, 1ll), min(i - a[i] + k, n), 1);
if (max(i - a[i] + k + 1, 1ll) <= n) modify(1, 1, n, max(i - a[i] + k + 1, 1ll), n, -1);
}
}
}
for (int i = 1; i <= n; i++) ans = min(ans, query(1, 1, n, 1, i));
cout << min(sum, sum + ans);
return 0;
}
P11188 「KDOI-10」商店砍价
思路
没有直接想出
代码
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 1e5 + 10, M = 11;
int id, T, n, sum, ans;
char c[N];
int a[N], v[M], dp[N][M], f[7];
bool check(int cnt){
for (int i = cnt, pos = 0; i >= 1; i--){
int p = dp[pos][f[i]];
if (p == 0) return 0;
pos = p;
}
return 1;
}
signed main(){
id = read(), T = read();
while (T--){
cin >> c + 1;
n = strlen(c + 1);
for (int i = 1; i <= 9; i++) v[i] = read();
for (int i = 1; i <= n; i++) a[i] = (c[i] ^ 48), sum += v[a[i]];
ans = sum;
for (int i = n; i >= 0; i--){
for (int j = 1; j <= 9; j++){
if (a[i + 1] == j) dp[i][j] = i + 1;
else dp[i][j] = dp[i + 1][j];
}
}
for (int i = 0; i <= 500000; i++){
int x = i, cnt = 0, s = sum;
while (x){
f[++cnt] = x % 10;
s -= v[f[cnt]];
x /= 10;
}
if (!check(cnt)) continue;
ans = min(ans, s + i);
}
cout << ans << endl;
sum = 0, ans = 0;
for (int i = 0; i <= n; i++)
for (int j = 1; j <= 9; j++) dp[i][j] = 0;
for (int i = 1; i <= n; i++) a[i] = 0;
for (int i = 1; i <= 9; i++) v[i] = 0;
}
return 0;
}
Bananas in a Microwave
思路
直接模拟复杂度会爆掉,于是考虑
代码
#include<iostream>
#include<cmath>
#define int long long
#define INF 0x3f3f3f3f
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 1e5 + 10;
int n, m;
int f[N], g[N];
signed main(){
n = read(), m = read();
for (int i = 1; i <= m; i++) f[i] = INF;
for (int i = 1; i <= n; i++){
int t = read(), x = read(), y = read();
double x0 = 1.0 * x / 100000;
for (int j = 1; j <= m; j++)
if (f[j] != INF) g[j] = 0;
else g[j] = INF;
if (t == 1){
for (int j = 0; j <= m; j++){
int k = ceil(j + x0);
if (k <= m && g[j] != INF)
g[k] = min(g[k], g[j] + 1);
}
}else{
for (int j = 1; j <= m; j++){
int k = ceil(1.0 * j * 1.0 * x / 100000);
if (k <= m && g[j] != INF)
g[k] = min(g[k], g[j] + 1);
}
}
for (int j = 1; j <= m; j++)
if (f[j] == INF && g[j] <= y) f[j] = i;
}
for (int i = 1; i <= m; i++)
if (f[i] == INF) cout << -1 << ' ';
else cout << f[i] << ' ';
return 0;
}
[ABC174F] Range Set Query
思路
莫队板子。莫队就是一个将询问离线下来的操作,然后用左右端点扩展或缩小区间。
代码
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 5e5 + 10;
int n, m, block, ans[N];
int a[N], b[N];
struct node{
int l, r, id;
bool operator < (const node &w) const{
return b[l] ^ b[w.l] ? b[l] < b[w.l] : (b[l] & 1 ? r < w.r : r > w.r);
}
}q[N];
int L = 1, R, cnt[N];
int main(){
n = read(), m = read(), block = sqrt(n);
for (int i = 1; i <= n; i++) a[i] = read(), b[i] = (i - 1) / block + 1;
for (int i = 1; i <= m; i++) q[i].l = read(), q[i].r = read(), q[i].id = i;
sort(q + 1, q + m + 1);
int res = 0;
for (int i = 1; i <= m; i++){
while (L > q[i].l) res += !cnt[a[--L]]++;
while (R < q[i].r) res += !cnt[a[++R]]++;
while (L < q[i].l) res -= !--cnt[a[L++]];
while (R > q[i].r) res -= !--cnt[a[R--]];
ans[q[i].id] = res;
}
for (int i = 1; i <= m; i++) cout << ans[i] << '\n';
return 0;
}
P4462 [CQOI2018] 异或序列
思路
由于异或有可逆性,那么对
注意到查询是区间,选择用莫队去做,莫队维护当前加进来的数与
代码
#include<iostream>
#include<cmath>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 1e5 + 10;
int n, m, k, block, ans[N];
int a[N], b[N];
struct node{
int l, r, id;
bool operator < (const node &x) const{
return b[l] ^ b[x.l] ? b[l] < b[x.l] : (b[l] & 1 ? r < x.r : r > x.r);
}
}q[N];
int L, R = -1, cnt[N], res;
void add(int x){
res += cnt[a[x] ^ k];
cnt[a[x]]++;
}
void del(int x){
cnt[a[x]]--;
res -= cnt[a[x] ^ k];
}
signed main(){
n = read(), m = read(), k = read(), block = sqrt(n);
for (int i = 1; i <= n; i++) a[i] = read(), a[i] ^= a[i - 1], b[i] = (i - 1) / block + 1;
for (int i = 1; i <= m; i++) q[i].l = read() - 1, q[i].r = read(), q[i].id = i;
sort(q + 1, q + m + 1);
for (int i = 1; i <= m; i++){
while (L > q[i].l) add(--L);
while (R < q[i].r) add(++R);
while (L < q[i].l) del(L++);
while (R > q[i].r) del(R--);
ans[q[i].id] = res;
}
for (int i = 1; i <= m; i++) cout << ans[i] << '\n';
return 0;
}
P9108 [PA2020] Malowanie płotu
思路
40pts
设
70pts
考虑在什么条件下才能够转移,很明显转移的
100pts
发现这样枚举左右端点是无法继续优化下去了,于是考虑将左右端点拆开进行动态规划,设
思考转移方程,需要用到容斥,
代码
#include<iostream>
#define int long long
using namespace std;
inline int read(){
register int x = 0, f = 1;
register char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
const int N = 1e7 + 10;
int n, m, mod, ans;
int f[N], g[N], sf[N], sumf[N], sg[N], sumg[N];
signed main(){
n = read(), m = read(), mod = read();
for (int i = 1; i <= m; i++) f[i] = i, g[i] = m - i + 1;
for (int i = 2; i <= n; i++){
for (int j = 1; j <= m; j++) sf[j] = (sf[j - 1] + f[j]) % mod, sumf[j] = (sumf[j - 1] + sf[j]) % mod;
for (int j = 1; j <= m; j++) sg[j] = (sg[j - 1] + g[j]) % mod;
for (int j = m; j >= 1; j--) sumg[j] = (sumg[j + 1] + (sg[m] - sg[j - 1] + mod) % mod) % mod;
for (int j = 1; j <= m; j++) f[j] = (j * sg[j] % mod - sumf[j - 1] + mod) % mod;
for (int j = 1; j <= m; j++) g[j] = ((m - j + 1) * ((sf[m] - sf[j - 1] + mod) % mod) % mod - sumg[j + 1] + mod) % mod;
}
for (int i = 1; i <= m; i++) ans = (ans + f[i]) % mod;
cout << ans;
return 0;
}
P4768 [NOI2018] 归程
思路
前置芝士:kruskal 重构树。
由 kruskal 重构树的性质得知,跑一遍最大生成树,建成的 kruskal 重构树,两个点的
题目显然要使汽车开一段路,再走最小的路程,那么,生成一个上述的 kruskal 重构树,就可以得知起始点与其他点之间简单路径的最小边权的最大值,如果它们两个点之间的
于是先求一遍最短路,再建一遍 kruskal 重构树,
代码
#include<iostream>
#include<algorithm>
#include<queue>
#include<climits>
#include<cstring>
#define INF INT_MAX
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 4e5 + 10, M = 4e5 + 10;
int T, n, m, Q, K, S, lastans;
struct edge{
int v, w, nxt;
}e[M << 1];
int head_edge[N], cnt_edge;
struct node{
int u, v, w;
bool operator < (const node &b) const{
return w > b.w;
}
}f[M];
void add_edge(int u, int v, int w){
e[++cnt_edge] = (edge){v, w, head_edge[u]};
head_edge[u] = cnt_edge;
}
void add_node(int u, int v, int w, int now){
f[now] = (node){u, v, w};
}
struct dij{
int u, d;
bool operator < (const dij &b) const{
return d > b.d;
}
};
priority_queue<dij> q;
int dis[N];
void dijkstra(int s){
for (int i = 1; i <= n; i++) dis[i] = INF;
dis[s] = 0;
q.push((dij){s, 0});
while (!q.empty()){
dij t = q.top();q.pop();
int u = t.u, d = t.d;
if (d != dis[u]) continue;
for (int i = head_edge[u]; i; i = e[i].nxt){
int v = e[i].v;
if (dis[v] > dis[u] + e[i].w){
dis[v] = dis[u] + e[i].w;
q.push((dij){v, dis[v]});
}
}
}
}
int fi[N];
int find(int x){
return (x == fi[x] ? fi[x] : fi[x] = find(fi[x]));
}
struct krus{
int v, nxt;
}kru[M << 1];
int head_kru[N], cnt_kru, val[N];
void add_kruskal(int u, int v){
kru[++cnt_kru] = (krus){v, head_kru[u]};
head_kru[u] = cnt_kru;
}
void kruskal(){
sort(f + 1, f + m + 1);
for (int i = 1; i <= (n << 1) - 1; i++) fi[i] = i;
int tot = 0;
for (int i = 1; i <= m; i++){
int eu = find(f[i].u), ev = find(f[i].v);
if (eu == ev) continue;
tot++;
fi[eu] = fi[ev] = n + tot;
add_kruskal(eu, n + tot), add_kruskal(n + tot, eu);
add_kruskal(ev, n + tot), add_kruskal(n + tot, ev);
val[n + tot] = f[i].w;
if (tot == n - 1) break;
}
}
int dp[N], fath[N][21];
void dfs(int u, int fa){
fath[u][0] = fa;
for (int i = head_kru[u]; i; i = kru[i].nxt){
int v = kru[i].v;
if (v == fa) continue;
dfs(v, u);
dp[u] = min(dp[u], dp[v]);
}
if (u <= n) dp[u] = dis[u];
}
void init(){
for (int i = 1; i <= 19; i++)
for (int j = 1; j <= (n << 1) - 1; j++) fath[j][i] = fath[fath[j][i - 1]][i - 1];
}
int query(int v, int p){
for (int i = 19; i >= 0; i--)
if (val[fath[v][i]] > p) v = fath[v][i];
return dp[v];
}
void solve(){
n = read(), m = read();
for (int i = 1; i <= m; i++){
int u = read(), v = read(), w = read(), l = read();
add_edge(u, v, w), add_edge(v, u, w), add_node(u, v, l, i);
}
dijkstra(1);
kruskal();
for (int i = 1; i <= (n << 1) - 1; i++) dp[i] = INF;
dfs(find(1), 0);
init();
Q = read(), K = read(), S = read();
while (Q--){
int v0 = read(), p0 = read(), v = (v0 + K * lastans - 1) % n + 1, p = (p0 + K * lastans) % (S + 1);
cout << (lastans = query(v, p)) << '\n';
}
lastans = 0, cnt_edge = 0, cnt_kru = 0;
memset(e, 0, sizeof e);
memset(head_edge, 0, sizeof head_edge);
memset(f, 0, sizeof f);
memset(dis, 0, sizeof dis);
memset(fi, 0, sizeof fi);
memset(kru, 0, sizeof kru);
memset(head_kru, 0, sizeof head_kru);
memset(val, 0, sizeof val);
memset(dp, 0, sizeof dp);
memset(fath, 0, sizeof fath);
}
int main(){
T = read();
while (T--) solve();
return 0;
}
Omkar and Tours
思路
考虑第一个问题非常好做,直接对所有询问离线下来,排个序,并查集维护连通块。
如何做第二个问题?对
代码
#include<iostream>
#include<algorithm>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 4e5 + 10;
int n, q, ans1[N], ans2[N];
int c[N];
struct node{
int u, v, w;
bool operator < (const node &b) const{
return w < b.w;
}
}f[N];
struct edge{
int u, v, w;
bool operator < (const edge &b) const{
return w > b.w;
}
}e[N];
int fi[N], val[N];
int find(int x){
return (x == fi[x] ? fi[x] : fi[x] = find(fi[x]));
}
struct kru{
int v, nxt;
}k[N << 1];
int head_kru[N], cnt_kru;
void add_kru(int u, int v){
k[++cnt_kru] = (kru){v, head_kru[u]};
head_kru[u] = cnt_kru;
}
void kruskal(){
sort(f + 1, f + n);
for (int i = 1; i < (n << 1); i++) fi[i] = i;
int tot = 0;
for (int i = 1; i < n; i++){
int eu = find(f[i].u), ev = find(f[i].v);
if (eu == ev) continue;
tot++;
fi[eu] = fi[ev] = n + tot;
add_kru(eu, n + tot), add_kru(n + tot, eu);
add_kru(ev, n + tot), add_kru(n + tot, ev);
val[n + tot] = f[i].w;
}
}
int fa[N][21], dep[N];
void dfs(int u, int fu){
fa[u][0] = fu;
dep[u] = dep[fu] + 1;
for (int i = head_kru[u]; i; i = k[i].nxt){
int v = k[i].v;
if (v == fu) continue;
dfs(v, u);
}
}
void init(){
for (int i = 1; i <= 19; i++)
for (int j = 1; j < (n << 1); j++) fa[j][i] = fa[fa[j][i - 1]][i - 1];
}
int lca(int x, int y){
if (dep[x] < dep[y]) swap(x, y);
for (int i = 19; i >= 0; i--)
if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];
if (x == y) return x;
for (int i = 19; i >= 0; i--)
if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
struct Query{
int c, x, id;
bool operator < (const Query &b) const{
return c > b.c;
}
}Q[N];
struct dsu{
int fa, mx, pos;
}d[N];
int fd(int x){
return (x == d[x].fa ? d[x].fa : d[x].fa = fd(d[x].fa));
}
void merge(int u, int v){
int eu = fd(u), ev = fd(v);
d[ev].fa = eu;
if (d[ev].mx > d[eu].mx){
d[eu].mx = d[ev].mx;
d[eu].pos = d[ev].pos;
}else if (d[eu].mx == d[ev].mx){
d[eu].pos = lca(d[eu].pos, d[ev].pos);
}
}
int main(){
n = read(), q = read();
for (int i = 1; i <= n; i++) c[i] = read();
for (int i = 1; i < n; i++){
int u = read(), v = read(), w = read(), t = read();
e[i] = (edge){u, v, w};
f[i] = (node){u, v, t};
}
kruskal();
dfs((n << 1) - 1, 0);
init();
for (int i = 1; i <= q; i++) Q[i] = (Query){read(), read(), i};
sort(e + 1, e + n);
sort(Q + 1, Q + q + 1);
for (int i = 1; i <= n; i++) d[i].fa = d[i].pos = i, d[i].mx = c[i];
for (int i = 1, j = 0; i <= q; i++){
while (e[j + 1].w >= Q[i].c && j < n){
j++;
merge(e[j].u, e[j].v);
}
int y = fd(Q[i].x);
ans1[Q[i].id] = d[y].mx;
ans2[Q[i].id] = val[lca(Q[i].x, d[y].pos)];
}
for (int i = 1; i <= q; i++) cout << ans1[i] << ' ' << ans2[i] << '\n';
return 0;
}
P5684 [CSP-J2019 江西] 非回文串
思路
考虑要求非回文串的个数,即为总串的个数减去回文串的个数,对于每个字符统计一遍出现次数,特判是否有回文串,然后枚举字符,求每个在前
代码
#include<iostream>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 2e3 + 10, mod = 1e9 + 7;
int n, ans = 1;
int cnt[28], ji, ou, mul[N << 1], inv[N << 1];
int qpow(int a, int b){
int ans = 1;
while (b){
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
void init(){
mul[0] = 1, inv[0] = 1;
for (int i = 1; i <= (n << 1); i++)
mul[i] = mul[i - 1] * i % mod, inv[i] = qpow(mul[i], mod - 2);
}
int C(int m, int n){
if (m > n) return 0;
return mul[n] * inv[n - m] % mod * inv[m] % mod;
}
signed main(){
n = read();
for (int i = 1; i <= n; i++){
char c;cin >> c;
cnt[c - 'a']++;
}
for (int i = 0; i < 26; i++)
if (cnt[i] & 1) ji++;
else ou++;
init();
if (n & 1 && ji != 1){
cout << mul[n] % mod;
return 0;
}
if (!(n & 1) && ji){
cout << mul[n] % mod;
return 0;
}
int res = 0;
for (int i = 0; i < 26; i++){
if (!cnt[i]) continue;
ans = ans * C(cnt[i] / 2, n / 2 - res) % mod * mul[cnt[i]] % mod;
res += cnt[i] / 2;
}
cout << (mul[n] - ans + mod) % mod;
return 0;
}
SUM and REPLACE
思路
有一个经典的套路就是,这种修改成约数的次数不会有几次,于是维护每个区间和与区间内有多少个
代码
#include<iostream>
#include<cmath>
#define ls now << 1
#define rs now << 1 | 1
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 3e5 + 10;
int n, m;
int a[N];
int d(int x){
int res = 0;
for (int i = 1; i < sqrt(x); i++)
if (x % i == 0) res += 2;
int q = sqrt(x);
if (q * q == x) res++;
return res;
}
struct tree{
long long v;
int s;
}t[N << 2];
void pushup(int now){
t[now].v = t[ls].v + t[rs].v;
t[now].s = t[ls].s + t[rs].s;
}
void build(int now, int l, int r){
if (l == r){
t[now] = (tree){a[l], (a[l] == 1 || a[l] == 2)};
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
pushup(now);
}
void modify(int now, int l, int r, int x, int y){
if (l == r){
t[now].v = d(t[now].v);
t[now].s = (t[now].v == 1 || t[now].v == 2);
return;
}
int mid = (l + r) >> 1;
if (x <= mid && t[ls].s != mid - l + 1) modify(ls, l, mid, x, y);
if (mid + 1 <= y && t[rs].s != r - mid) modify(rs, mid + 1, r, x, y);
pushup(now);
}
long long query(int now, int l, int r, int x, int y){
if (x <= l && r <= y){
return t[now].v;
}
int mid = (l + r) >> 1;
long long res = 0;
if (x <= mid) res += query(ls, l, mid, x, y);
if (mid + 1 <= y) res += query(rs, mid + 1, r, x, y);
return res;
}
int main(){
n = read(), m = read();
for (int i = 1; i <= n; i++) a[i] = read();
build(1, 1, n);
for (int i = 1; i <= m; i++){
int opt = read(), l = read(), r = read();
if (opt == 1){
modify(1, 1, n, l, r);
}else{
cout << query(1, 1, n, l, r) << '\n';
}
}
return 0;
}
[AGC029C] Lexicographic constraints
思路
首先二分答案,将二分得到的答案抽象成一个
代码
#include<iostream>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 2e5 + 10;
int n, sum, l = 2, r, ans;
int a[N], stk[N], num[N], top;
void add(int pos, int k){
while (stk[top] > pos) top--;
if (stk[top] == pos) num[top]++;
else stk[++top] = pos, num[top] = 1;
if (top > 1 && num[top] == k) top--, add(pos - 1, k);
}
bool check(int x){
top = 0;
stk[++top] = 0, num[top] = 0;
for (int i = 2; i <= n; i++)
if (a[i] <= a[i - 1]) add(a[i], x);
return (num[1] == 0);
}
int main(){
n = read();
for (int i = 1; i <= n; i++) a[i] = read(), r = max(r, a[i]), sum += (a[i] > a[i - 1]);
if (sum == n){
cout << 1;
return 0;
}
r = max(r, n);
while (l <= r){
int mid = (l + r) >> 1;
if (check(mid)) r = mid - 1, ans = mid;
else l = mid + 1;
}
cout << ans;
return 0;
}
The Child and Sequence
思路
维护区间最大值,如果小于
代码
#include<iostream>
#define int long long
#define ls now << 1
#define rs now << 1 | 1
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 1e5 + 10;
int n, m;
int a[N];
struct tree{
int sum, max;
}t[N << 2];
void pushup(int now){
t[now].sum = t[ls].sum + t[rs].sum;
t[now].max = max(t[ls].max, t[rs].max);
}
void build(int now, int l, int r){
if (l == r){
t[now].sum = t[now].max = a[l];
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
pushup(now);
}
void modify_mod(int now, int l, int r, int x, int y, int k){
if (l == r){
t[now].sum %= k;
t[now].max = t[now].sum;
return;
}
int mid = (l + r) >> 1;
if (x <= mid && t[ls].max >= k) modify_mod(ls, l, mid, x, y, k);
if (mid + 1 <= y && t[rs].max >= k) modify_mod(rs, mid + 1, r, x, y, k);
pushup(now);
}
void modify(int now, int l, int r, int x, int k){
if (l == r){
t[now].sum = t[now].max = k;
return;
}
int mid = (l + r) >> 1;
if (x <= mid) modify(ls, l, mid, x, k);
else modify(rs, mid + 1, r, x, k);
pushup(now);
}
int query(int now, int l, int r, int x, int y){
if (x <= l && r <= y){
return t[now].sum;
}
int mid = (l + r) >> 1, res = 0;
if (x <= mid) res += query(ls, l, mid, x, y);
if (mid + 1 <= y) res += query(rs, mid + 1, r, x, y);
return res;
}
signed main(){
n = read(), m = read();
for (int i = 1; i <= n; i++) a[i] = read();
build(1, 1, n);
for (int i = 1; i <= m; i++){
int opt = read();
if (opt == 1){
int l = read(), r = read();
cout << query(1, 1, n, l, r) << '\n';
}else if (opt == 2){
int l = read(), r = read(), x = read();
modify_mod(1, 1, n, l, r, x);
}else{
int x = read(), k = read();
modify(1, 1, n, x, k);
}
}
return 0;
}
[ABC203D] Pond
思路
二分中位数
代码
#include<iostream>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 810;
int n, k, ans;
int a[N][N], b[N][N];
bool check(int x){
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + (a[i][j] > x);
for (int i = k; i <= n; i++)
for (int j = k; j <= n; j++)
if (b[i][j] - b[i - k][j] - b[i][j - k] + b[i - k][j - k] <= k * k / 2) return 1;
return 0;
}
int main(){
n = read(), k = read();
int l = 0, r = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) a[i][j] = read(), r = max(r, a[i][j]);
while (l <= r){
int mid = (l + r) >> 1;
if (check(mid)){
r = mid - 1;
ans = mid;
}else l = mid + 1;
}
cout << ans;
return 0;
}
Another MEX Problem
思路
显然有一个
考虑优化这个
代码
#include<iostream>
#include<vector>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 5e3 + 10, M = 8.2e3 + 10;
int T, n, ans;
int a[N], mex[N][N];
bool vis[M], dp[N][M];
vector<int> v[N];
void solve(){
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1; i <= n; i++){
int val = 0;
for (int j = i; j <= n; j++){
vis[a[j]] = 1;
while (vis[val]) val++;
mex[i][j] = val;
}
for (int j = 0; j <= M - 10; j++) vis[j] = 0;
}
for (int i = 1; i <= n; i++){
for (int j = i; j <= n; j++){
if (mex[i][j] != mex[i + 1][j] && mex[i][j] != mex[i][j - 1]) v[j].emplace_back(i);
}
}
dp[0][0] = 1;
for (int i = 1; i <= n; i++){
for (int j = 0; j <= M - 10; j++){
dp[i][j] |= dp[i - 1][j];
for (auto l : v[i]) if ((j ^ mex[l][i]) <= M - 10) dp[i][j] |= dp[l - 1][j ^ mex[l][i]];
}
}
for (int i = 1; i <= n; i++) v[i].clear();
for (int i = M - 10; i >= 0; i--){
if (dp[n][i]){
ans = i;
break;
}
}
cout << ans << '\n';
ans = 0;
for (int i = 0; i <= n; i++)
for (int j = 0; j <= M - 10; j++) dp[i][j] = 0;
}
int main(){
T = read();
while (T--){
solve();
}
return 0;
}
[ABC292Ex] Rating Estimator
思路
题目要求找到第一个
代码
#include<iostream>
#include<iomanip>
#include<algorithm>
#define pii make_pair
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 5e5 + 10;
int n, p, m;
int a[N], sum[N];
struct tree{
int max, tag;
}t[N << 2];
void pushup(int now){
t[now].max = max(t[now << 1].max, t[now << 1 | 1].max);
}
void pushdown(int now){
t[now << 1].max += + t[now].tag;
t[now << 1 | 1].max += t[now].tag;
t[now << 1].tag += t[now].tag;
t[now << 1 | 1].tag += t[now].tag;
t[now].tag = 0;
}
void build(int now, int l, int r){
if (l == r){
t[now].max = sum[l];
return;
}
int mid = (l + r) >> 1;
build(now << 1, l, mid);
build(now << 1 | 1, mid + 1, r);
pushup(now);
}
void modify(int now, int l, int r, int x, int y, int k){
if (x <= l && r <= y){
t[now].max += k;
t[now].tag += k;
return;
}
pushdown(now);
int mid = (l + r) >> 1;
if (x <= mid) modify(now << 1, l, mid, x, y, k);
if (mid + 1 <= y) modify(now << 1 | 1, mid + 1, r, x, y, k);
pushup(now);
}
pair<int, int> query(int now, int l, int r, int x, int y){
if (l == r){
return pii(t[now].max, l);
}
pushdown(now);
int mid = (l + r) >> 1;
if (t[now << 1].max >= 0) return query(now << 1, l, mid, x, y);
else return query(now << 1 | 1, mid + 1, r, x, y);
}
signed main(){
n = read(), p = read(), m = read();
for (int i = 1; i <= n; i++) a[i] = read() - p, sum[i] = sum[i - 1] + a[i];
build(1, 1, n);
for (int i = 1; i <= m; i++){
int x = read(), k = read();
modify(1, 1, n, x, n, -a[x]), modify(1, 1, n, x, n, k - p), a[x] = k - p;
pair<int, int> ans = query(1, 1, n, 1, n);
int pos = ans.second;
cout << fixed << setprecision(15) << (long double)ans.first / (long double)pos + p << '\n';
}
return 0;
}
Common Divisor Graph
思路
集中注意力发现,最多创造两个新的节点就能使
当答案为
当答案为
当答案为
代码
#include<iostream>
#include<vector>
#include<map>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 1.5e5 + 10, M = 1e6 + 10;
int n, m, maxn;
int a[N], prime[M], cnt, fa[M];
bool p[M];
vector<int> v[M];
void init(){
for (int i = 2; i <= maxn; i++){
if (!p[i]) prime[++cnt] = i;
for (int j = 1; j <= cnt && i * prime[j] <= maxn; j++){
p[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
for (int i = 1; i <= maxn; i++) fa[i] = i;
}
int find(int x){
return (x == fa[x] ? fa[x] : fa[x] = find(fa[x]));
}
void merge(int x, int y){
int fx = find(x), fy = find(y);
fa[fx] = fy;
}
map<int, map<int, bool> > vis;
int main(){
n = read(), m = read();
for (int i = 1; i <= n; i++) a[i] = read(), maxn = max(maxn, a[i] + 1);
init();
for (int i = 1; i <= cnt; i++)
for (int j = prime[i]; j <= maxn; j += prime[i]) v[j].emplace_back(prime[i]);
for (int i = 1; i <= n; i++)
for (auto j : v[a[i]]) merge(a[i], j);
for (int i = 1; i <= n; i++){
for (int j = 0; j < v[a[i]].size(); j++)
for (int k = j; k < v[a[i] + 1].size(); k++){
int fx = find(v[a[i]][j]), fy = find(v[a[i] + 1][k]);
if (fx == fy) continue;
vis[fx][fy] = 1;
}
for (int j = 0; j < v[a[i] + 1].size(); j++){
for (int k = j; k < v[a[i] + 1].size(); k++){
int fx = find(v[a[i] + 1][j]), fy = find(v[a[i] + 1][k]);
if (fx == fy) continue;
vis[fx][fy] = 1;
}
}
}
for (int i = 1; i <= m; i++){
int s = find(a[read()]), t = find(a[read()]);
if (s == t) cout << 0 << '\n';
else if (vis[s][t] || vis[t][s]) cout << 1 << '\n';
else cout << 2 << '\n';
}
return 0;
}
[ABC376E] Max × Sum
思路
以
代码
#include<iostream>
#include<algorithm>
#include<queue>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 2e5 + 10;
int T, n, k, ans;
struct node{
int a, b;
bool operator < (const node &y) const{
return a < y.a;
}
}x[N];
priority_queue<int> q;
void solve(){
n = read(), k = read();
for (int i = 1; i <= n; i++) x[i].a = read();
for (int i = 1; i <= n; i++) x[i].b = read();
sort(x + 1, x + n + 1);
int s = 0;
for (int i = 1; i <= k; i++) q.push(x[i].b), s += x[i].b;
ans = s * x[k].a;
for (int i = k + 1; i <= n; i++){
if (x[i].b < q.top()){
s -= q.top();
s += x[i].b;
q.pop();
q.push(x[i].b);
ans = min(ans, s * x[i].a);
}
}
cout << ans << '\n';
while (!q.empty()) q.pop();
}
signed main(){
T = read();
while (T--) solve();
return 0;
}
[ABC377G] Edit to Match
思路
考虑建一颗 trie 树,每次插入一个字符串,求其末尾到其他字符串末尾的最小长度,设
代码
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 2e5 + 10, M = 27;
int n, tot;
char c[N];
int trie[N][M], f[N * M], res[N * M];
int insert(char c[]){
int m = strlen(c), now = 0, p = 0, ans;
for (int i = 0; i < m; i++){
int &to = trie[now][c[i] - 'a'];
if (!to) to = ++tot, f[tot] = 0x7fffffff;
f[to] = min(f[to], f[now] + 1);
now = to;
res[++p] = now;
}
ans = f[now];
f[now] = 0;
for (int i = p - 1; i >= 1; i--) f[res[i]] = min(f[res[i]], f[res[i + 1]] + 1);
return ans;
}
int main(){
n = read();
for (int i = 1; i <= n; i++){
cin >> c;
cout << insert(c) << '\n';
}
return 0;
}
[ABC377E] Permute K times 2
思路
对于当前
代码
#include<iostream>
#include<vector>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 2e5 + 10;
int n, k;
int a[N], vis[N], pos[N];
vector<int> g[N];
void dfs(int u, int top){
if (vis[u]) return;
vis[u] = top;
g[top].emplace_back(u);
pos[u] = g[top].size();
dfs(a[u], top);
}
int qpow(int a, int b, int mod){
int ans = 1;
while (b){
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
signed main(){
n = read(), k = read();
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1; i <= n; i++)
if (!vis[a[i]]) dfs(a[i], a[i]);
for (int i = 1; i <= n; i++){
int mod = g[vis[a[i]]].size(), p = ((pos[a[i]] + qpow(2, k, mod)) % mod - 1 + mod) % mod;
p = (p == 0 ? mod : p);
cout << g[vis[a[i]]][p - 1] << ' ';
}
return 0;
}
P7981 [JRKSJ R3] system
思路
这个题目和 ABC377E 有些不同,但也有相同的思路,考虑对
对于当前
接下来考虑基环树,我们发现这是一个内向基环树,按照上面的结论,从自己一直走
代码
#include<iostream>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 5e5 + 10, M = 21;
int n, k;
int a[N], fa[N][M], vis[N], cir[N];
void dfs(int u, int id){
if (vis[u] == id){
if (cir[u]) return;
int x = u, num = 1;
while (a[x] != u){
x = a[x];
if (cir[x]) return;
num++;
}
x = u, cir[x] = num;
while (a[x] != u){
x = a[x];
if (cir[x]) return;
cir[x] = num;
}
return;
}
if (vis[u]) return;
vis[u] = id;
fa[u][0] = a[u];
dfs(a[u], id);
}
void init(){
for (int i = 1; i <= 19; i++)
for (int j = 1; j <= n; j++) fa[j][i] = fa[fa[j][i - 1]][i - 1];
}
int qpow(int a, int b, int mod){
int ans = 1;
while (b){
if (b & 1){
ans = ans * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return ans;
}
signed main(){
n = read(), k = read();
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1; i <= n; i++)
if (!vis[a[i]]) dfs(a[i], i);
init();
for (int i = 1; i <= n; i++){
int p = a[i], j;
for (j = 0; j <= 19 && !cir[p]; j++) p = fa[p][j];
int mod = cir[p], turn = ((((qpow(2, k, mod) - 1 + mod) % mod - qpow(2, j, mod) + mod) + mod) % mod + 1) % mod;
for (j = 19; j >= 0; j--){
if (turn >= (1 << j)) turn -= (1 << j), p = fa[p][j];
}
cout << p << ' ';
}
return 0;
}
[ABC310E] NAND repeatedly
思路
由于没有交换律,所以从前往后扫,计算每个前缀的贡献,若遇到
代码
#include<iostream>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int n, ans;
char c[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> (c + 1);
int cnt = 0;
for (int i = 1; i <= n; i++){
if (c[i] == '1') cnt = i - cnt;
else cnt = i - 1;
ans += cnt;
}
cout << ans;
return 0;
}
[ABC323E] Playlist
思路
概率
代码
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 1e3 + 10, M = 1e4 + 10, mod = 998244353;
int n, k, ans;
int t[N], f[M];
int qpow(int a, int b){
int ans = 1;
while (b){
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
signed main(){
n = read(), k = read();
for (int i = 1; i <= n; i++) t[i] = read();
int inv = qpow(n, mod - 2);
f[0] = 1;
for (int i = 0; i <= k; i++)
for (int j = 1; j <= n; j++)
if (i >= t[j]) f[i] = (f[i] + f[i - t[j]] * inv % mod) % mod;
for (int i = max(k - t[1] + 1, 0ll); i <= k; i++) ans = (ans + f[i] * inv % mod) % mod;
cout << ans;
return 0;
}
[ARC061F] 3人でカードゲーム
思路
将题目要求的东西转化一下,变为有这样的取牌序列,满足第一个牌堆先被拿完,求这样的取牌序列的个数。
设一个刚好拿完牌堆
枚举拿出了
前面的组合数表示选出
这样做是
会有不符合条件的组合数,它们的贡献为
那么答案:
代码
#include<iostream>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
inline void write(int x){if (x < 0) putchar('-'), x = -x;if (x > 9) write(x / 10);putchar(x % 10 + '0');}
const int N = 1e6 + 10, mod = 1e9 + 7;
int n, m, k, ans;
int mul[N], inv[N], s[N];
int qpow(int a, int b){
int ans = 1;
while (b){
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
void init(){
mul[0] = inv[0] = 1;
for (int i = 1; i <= n + m + k; i++) mul[i] = mul[i - 1] * i % mod, inv[i] = qpow(mul[i], mod - 2);
}
int C(int m, int n){
if (m > n || m < 0 || n < 0) return 0;
return mul[n] * inv[n - m] % mod * inv[m] % mod;
}
signed main(){
n = read(), m = read(), k = read();
init();
s[0] = 1;
for (int i = 1; i <= m + k; i++) s[i] = ((s[i - 1] * 2 % mod - C(i - 1 - k, i - 1) + mod) % mod - C(m, i - 1) + mod) % mod;
for (int i = 0; i <= m + k; i++){
ans = (ans + qpow(3, m + k - i) * C(i, n + i - 1) % mod * s[i] % mod) % mod;
}
cout << ans;
return 0;
}
Girl Permutation
思路
观察题目性质,前后肯定是分开计算的,如果前缀最大值的位置第一个不为
先考虑前面的,前缀最大值最后一个位置上绝对是最大的数,从这个位置开始,有
代码
#include<iostream>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 2e5 + 10, K = 4e5, mod = 1e9 + 7;
int T, n, m1, m2;
int mul[N << 1], inv[N << 1];
int qpow(int a, int b){
int ans = 1;
while (b){
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
void init(){
mul[0] = inv[0] = 1;
for (int i = 1; i <= K; i++) mul[i] = mul[i - 1] * i % mod, inv[i] = qpow(mul[i], mod - 2);
}
int C(int m, int n){
return mul[n] * inv[n - m] % mod * inv[m] % mod;
}
int q[N], h[N];
void solve(){
n = read(), m1 = read(), m2 = read();
for (int i = 1; i <= m1; i++) q[i] = read();
for (int i = 1; i <= m2; i++) h[i] = read();
if (q[1] != 1 || h[m2] != n || q[m1] != h[1]){
cout << 0 << '\n';
return;
}
int ans = C(q[m1] - 1, n - 1);
for (int i = m1 - 1; i >= 1; i--){
ans = ans * C(q[i] - 1, q[i + 1] - 2) % mod;
ans = ans * mul[q[i + 1] - q[i] - 1] % mod;
}
for (int i = 2; i <= m2; i++){
ans = ans * C(n - h[i], n - h[i - 1] - 1) % mod;
ans = ans * mul[h[i] - h[i - 1] - 1] % mod;
}
cout << ans << '\n';
}
signed main(){
init();
T = read();
while (T--) solve();
return 0;
}
[ABC318E] Sandwiches
思路
正着做不好动态维护,于是考虑反着做。考虑加入了一个数,在它的的后面有很多与它相同的数,于是思考如何从上一个相同的数的答案转移过来,首先答案要加上上一个的答案,因为你可以看成着一个数就相当于上一个数,前提是忽略这个数和上一个数之间不同的数,如果有不同的数,这个数可以与中间不同的数和后面任意一个相同的数匹配,于是乘法原理计算即可。
代码
#include<iostream>
#include<vector>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 3e5 + 10;
int n, ans;
int a[N];
vector<int> g[N];
int res[N];
signed main(){
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = n; i >= 1; i--){
if (!g[a[i]].empty()) res[a[i]] += g[a[i]].size() * (g[a[i]].back() - i - 1);
g[a[i]].emplace_back(i);
ans += res[a[i]];
}
cout << ans;
return 0;
}
P1282 多米诺骨牌
思路
非常有意思的背包题目。
如果多米诺牌上面的数的和确定了下面数的和也就确定了,差值也能求出来,于是直接背包凑出上面的数需要最小的代价,最后扫一遍统计答案即可。
代码
#include<iostream>
#include<cmath>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 1e3 + 10;
int n, sum, res = 0x7fffffff, p;
int a[N], b[N], dp[N][N * 6];
signed main(){
n = read();
for (int i = 1; i <= n; i++) a[i] = read(), b[i] = read(), sum += a[i] + b[i];
for (int i = 0; i <= n; i++)
for (int j = 0; j <= 6 * n; j++) dp[i][j] = 0x7fffffff;
dp[0][0] = 0;
for (int i = 1; i <= n; i++){
for (int j = 0; j <= 6 * n; j++){
if (j - a[i] >= 0) dp[i][j] = min(dp[i][j], dp[i - 1][j - a[i]]);
if (j - b[i] >= 0) dp[i][j] = min(dp[i][j], dp[i - 1][j - b[i]] + 1);
}
}
for (int i = n; i <= 6 * n; i++)
if (dp[n][i] != 0x3f3f3f3f){
if (res >= abs(i - (sum - i))){
res = abs(i - (sum - i));
p = i;
}
}
cout << dp[n][p];
return 0;
}
[ABC369G] As far as possible
思路
仔细想一想发现并不是博弈论,第一个人每次需要选择离上次选取的所有点行走最远的点,第二个人需要走最小路径,考虑这个最小路径怎么来的,发现就是每条走过的边的边权乘以
代码
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 2e5 + 10;
int n, ans;
struct edge{
int v, w, nxt;
}e[N << 1];
int head[N], cnt;
void add(int u, int v, int w){
e[++cnt] = (edge){v, w, head[u]};
head[u] = cnt;
}
int sz[N], son[N], val[N], f[N];
void dfs1(int u, int fa, int w){
sz[u] = w;
f[u] = fa;
for (int i = head[u]; i; i = e[i].nxt){
int v = e[i].v;
if (v == fa) continue;
dfs1(v, u, val[v] = e[i].w);
if (sz[v] > sz[son[u]]) son[u] = v;
}
sz[u] += sz[son[u]];
}
int p[N];
void dfs2(int u, int t){
p[t] += val[u];
if (son[u]) dfs2(son[u], t);
for (int i = head[u]; i; i = e[i].nxt){
int v = e[i].v;
if (v == f[u] || v == son[u]) continue;
dfs2(v, v);
}
}
signed main(){
n = read();
for (int i = 1; i < n; i++){
int u = read(), v = read(), w = read();
add(u, v, w), add(v, u, w);
}
dfs1(1, 0, 0);
dfs2(1, 1);
sort(p + 1, p + n + 1, greater<int>());
for (int i = 1; i <= n; i++){
ans += p[i];
cout << (ans << 1) << '\n';
}
return 0;
}
[ARC183C] Not Argmax
思路
注意到
设
下面来解释一下这个递推式,等号左边是枚举
递推式解决了,还要知道那些
如何处理一个位置
代码
#include<iostream>
#include<bitset>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 510, M = 1e5 + 10, mod = 998244353;
int n, m;
bitset<N> g[N][N];
int dp[N][N];
int mul[N], inv[N];
int qpow(int a, int b){
int ans = 1;
while (b){
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
void init(){
for (int len = 2; len <= n; len++)
for (int l = 1; l + len - 1 <= n; l++){
int r = l + len - 1;
g[l][r] |= g[l + 1][r] | g[l][r - 1];
}
mul[0] = inv[0] = 1;
for (int i = 1; i <= n; i++) mul[i] = mul[i - 1] * i % mod, inv[i] = qpow(mul[i], mod - 2);
}
int C(int n, int m){
return mul[n] * inv[n - m] % mod * inv[m] % mod;
}
signed main(){
n = read(), m = read();
for (int i = 1; i <= m; i++){
int l = read(), r = read(), x = read();
if (l == r){
cout << '0';
return 0;
}
g[l][r][x] = 1;
}
init();
for (int i = 0; i <= n; i++) dp[i][i] = dp[i + 1][i] = 1;
for (int len = 2; len <= n; len++){
for (int l = 1; l + len - 1 <= n; l++){
int r = l + len - 1;
for (int k = l; k <= r; k++){
if (g[l][r][k]) continue;
dp[l][r] = (dp[l][r] + dp[l][k - 1] * dp[k + 1][r] % mod * C(r - l, k - l) % mod) % mod;
}
}
}
cout << dp[1][n];
return 0;
}
LCM Sum (hard version)
思路
正着做不好做,考虑用总数减去不好的三元组的数量,即
由于
如何统计答案?首先总的三元组的数量为
接下来考虑如何统计等于
考虑暴力枚举二元组不优秀,因为
代码
#include<iostream>
#include<vector>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 2e5 + 10, M = 1e7 + 10, K = 2e5;
int T;
vector<int> g[N];
void init(){
for (int i = 2; i <= K; i++){
for (int j = 1; j * j <= i; j++){
if (i % j == 0){
g[i].emplace_back(j);
if (j * j != i && j != 1) g[i].emplace_back(i / j);
}
}
}
for (int i = 2; i <= K; i++) sort(g[i].begin(), g[i].end());
}
struct node{
int l, r, id, res;
}q[N];
struct Query{
int x, y, cnt, id, opt;
bool operator < (const Query &b) const{
return (x == b.x ? opt < b.opt : x < b.x);
}
}p[M];
int cnt;
void get(){
for (int i = 2; i <= K; i++){
int h = g[i].size();
for (int j = 0; j < h; j++){
p[++cnt] = (Query){g[i][j], i, h - j - 1, 0, 0};
}
}
}
int t[N];
int lowbit(int x){return x & -x;}
void add(int x, int k){
for (int i = x; i <= K; i += lowbit(i)) t[i] += k;
}
int query(int x){
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += t[i];
return res;
}
signed main(){
T = read();
init();
for (int i = 1; i <= T; i++){
int l = read(), r = read();
q[i].l = l, q[i].r = r, q[i].id = i;
q[i].res = (r - l + 1) * (r - l) * (r - l - 1) / 6;
q[i].res -= max(r / 6 - (l - 1) / 3, 0ll);
q[i].res -= max(r / 15 - (l - 1) / 6, 0ll);
}
get();
for (int i = 1; i <= T; i++){
p[++cnt] = (Query){q[i].r, q[i].r, 0, q[i].id, 1};
p[++cnt] = (Query){q[i].r, q[i].l - 1, 0, q[i].id, 2};
p[++cnt] = (Query){q[i].l - 1, q[i].r, 0, q[i].id, 2};
p[++cnt] = (Query){q[i].l - 1, q[i].l - 1, 0, q[i].id, 1};
}
sort(p + 1, p + cnt + 1);
for (int i = 1; i <= cnt; i++){
if (p[i].opt == 0) add(p[i].y, p[i].cnt);
else if (p[i].opt == 1) q[p[i].id].res -= query(p[i].y);
else q[p[i].id].res += query(p[i].y);
}
for (int i = 1; i <= T; i++) cout << q[i].res << '\n';
return 0;
}
P6105 [Ynoi2010] y-fast trie
思路
分情况讨论,我们把加入集合的数
代码
#include<iostream>
#include<set>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
int n, mod, lastans;
multiset<int> s, k;
int find(int x, bool f){
if (x == -1) return -1;
multiset<int>::iterator it = s.upper_bound(mod - 1 - x);
if (it == s.begin()) return -1;
it--;
if (f && x == *it && s.count(x) == 1){
if (it == s.begin()) return -1;
return *--it;
}
return *it;
}
void insert(int x){
if (s.empty()){
s.insert(x);
return;
}
int y = find(x, 0), z = find(y, 1), p = find(z, 1);
if (y != -1 && x > z){
if (z != -1 && y == p) k.erase(k.find(y + z));
k.insert(x + y);
}
s.insert(x);
}
void del(int x){
s.erase(s.find(x));
if (s.empty()) return;
int y = find(x, 0), z = find(y, 1), p = find(z, 1);
if (y != -1 && x > z){
if (z != -1 && y == p) k.insert(y + z);
k.erase(k.find(x + y));
}
}
int query(){
multiset<int>::iterator it = --s.end();
int res;
if (s.count(*it) >= 2) res = 2 * (*it) % mod;
else res = (*it + *--it) % mod;
if (!k.empty()) res = max(res, *--k.end());
return res;
}
int main(){
n = read(), mod = read();
for (int i = 1; i <= n; i++){
int opt = read(), x = (read() ^ lastans) % mod;
if (opt == 1) insert(x);
else del(x);
if (s.size() <= 1) cout << "EE\n", lastans = 0;
else cout << (lastans = query()) << '\n';
}
return 0;
}
P7091 数上的树
思路
设
设
这样做的时间复杂度为
代码
#include<iostream>
#include<map>
#include<cmath>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 1e5 + 10;
int n, m;
int d[N], cnt, dp[N], g[N];
map<int, bool> mp;
void init(){
for (int i = 1; i * i < n; i++)
if (n % i == 0)
d[++cnt] = i, d[++cnt] = n / i;
int x = sqrt(n);
if (x * x == n) d[++cnt] = x;
}
signed main(){
n = read(), m = read();
init();
for (int i = 1; i <= m; i++) mp[read()] = 1;
sort(d + 1, d + cnt + 1);
for (int i = 1; i <= cnt; i++){
if (mp[d[i]]) dp[i] = -1;
else dp[i] = -114514;
}
for (int i = 1; i <= cnt; i++){
if (dp[i] == -1) continue;
bool f = 0;
for (int j = 2; j < i; j++)
if (d[i] % d[j] == 0) f = 1;
if (!f) g[i] = 1, dp[i] = d[i];
}
for (int i = 2; i <= cnt; i++){
for (int j = 2, k = i - 1; j < i; j++){
while (k >= j && d[j] * d[k] > d[i]) k--;
if (k < j) break;
if (d[i] == d[j] * d[k] && dp[i] != -1 && dp[j] != -1 && dp[k] != -1 && dp[j] != -114514 && dp[k] != -114514){
g[i] = g[j] + g[k] + 1;
if (dp[i] == -114514) dp[i] = dp[j] + dp[k] + (g[j] * g[k] + g[i]) * d[i];
else dp[i] = min(dp[i], dp[j] + dp[k] + (g[j] * g[k] + g[i]) * d[i]);
}
}
}
cout << (dp[cnt] == -114514 ? -1 : dp[cnt]);
return 0;
}
Card Bag
思路
考虑概率
代码
#include<iostream>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 5e3 + 10, mod = 998244353;
int n, ans;
int p[N], dp[N][N], sum[N][N], inv[N];
int qpow(int a, int b){
int ans = 1;
while (b){
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
void init(){
inv[0] = 1;
for (int i = 1; i <= n; i++) inv[i] = qpow(i, mod - 2);
}
signed main(){
n = read();
for (int i = 1; i <= n; i++) p[read()]++;
init();
for (int i = 0; i <= n; i++) sum[i][0] = 1;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= i; j++){
dp[i][j] = sum[i - 1][j - 1] * p[i] % mod * inv[n - j + 1] % mod;
ans = (ans + dp[i][j] * ((p[i] % mod - 1 + mod) % mod) % mod * inv[n - j] % mod) % mod;
sum[i][j] = (sum[i - 1][j] + dp[i][j]) % mod;
}
}
cout << ans;
return 0;
}
Blood Cousins
思路
为此特意去学了
求出每个询问的
代码
#include<iostream>
#include<vector>
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 1e5 + 10;
int n, m;
int f[N][23];
struct edge{
int v, nxt;
}e[N];
int head[N], cnt;
void add(int u, int v){
e[++cnt] = (edge){v, head[u]};
head[u] = cnt;
}
void init(){
for (int i = 1; i <= 20; i++)
for (int j = 1; j <= n; j++) f[j][i] = f[f[j][i - 1]][i - 1];
}
int dep[N], sz[N], son[N];
void dfs(int u, int fa){
dep[u] = dep[fa] + 1, sz[u] = 1;
for (int i = head[u]; i; i = e[i].nxt){
int v = e[i].v;
dfs(v, u);
sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
int find(int u, int k){
int d = dep[u];
for (int i = 20; i >= 0; i--)
if (d - dep[f[u][i]] < k) u = f[u][i];
return (u = f[u][0]);
}
struct query{
int x, res;
}ans[N];
vector<int> g[N];
int res[N];
void calc(int u, int s, int k){
res[dep[u]] += k;
for (int i = head[u]; i; i = e[i].nxt){
int v = e[i].v;
if (v == s) continue;
calc(v, s, k);
}
}
void dsu(int u, bool p){
for (int i = head[u]; i; i = e[i].nxt){
int v = e[i].v;
if (v == son[u]) continue;
dsu(v, 0);
}
if (son[u]) dsu(son[u], 1);
calc(u, son[u], 1);
for (int i = 0; i < g[u].size(); i++)
ans[g[u][i]].res = res[dep[ans[g[u][i]].x]] - 1;
if (!p) calc(u, 0, -1);
}
int main(){
n = read();
for (int i = 1; i <= n; i++){
f[i][0] = read();
if (f[i][0]) add(f[i][0], i);
}
init();
for (int i = 1; i <= n; i++)
if (!f[i][0]) dfs(i, 0);
m = read();
for (int i = 1; i <= m; i++){
int u = read(), p = read();
ans[i] = (query){u, 0};
g[find(u, p)].emplace_back(i);
}
for (int i = 1; i <= n; i++)
if (!f[i][0]) dsu(i, 0);
for (int i = 1; i <= m; i++) cout << ans[i].res << ' ';
return 0;
}
P3586 [POI2015] LOG
思路
如果大于等于
代码
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){register int x = 0, f = 1;register char c = getchar();while (c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return x * f;}
const int N = 1e6 + 10;
int n, m;
int a[N], t1[N], t2[N], l[N], cnt, len, p[N];
struct node{
char opt;
int x, y;
}q[N];
int lowbit(int x){return x & -x;}
void add(int t[], int x, int k){
for (int i = x; i <= len; i += lowbit(i)) t[i] += k;
}
int query(int t[], int x){
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += t[i];
return res;
}
signed main(){
n = read(), m = read();
l[++cnt] = 0;
for (int i = 1; i <= m; i++){
cin >> q[i].opt; q[i].x = read(), q[i].y = read();
l[++cnt] = q[i].y;
}
sort(l + 1, l + cnt + 1);
len = unique(l + 1, l + cnt + 1) - l - 1;
for (int i = 1; i <= n; i++) a[i] = 1, add(t1, 1, 1);
for (int i = 1; i <= m; i++){
int x = lower_bound(l + 1, l + len + 1, q[i].y) - l;
p[x] = q[i].y;
q[i].y = x;
}
for (int i = 1; i <= m; i++){
if (q[i].opt == 'U'){
add(t1, a[q[i].x], -1), add(t1, q[i].y, 1);
add(t2, a[q[i].x], -p[a[q[i].x]]), add(t2, q[i].y, p[q[i].y]);
a[q[i].x] = q[i].y;
}else{
int cnt = query(t1, len) - query(t1, q[i].y - 1), sum = query(t2, q[i].y - 1);
if (sum + cnt * p[q[i].y] >= q[i].x * p[q[i].y]) cout << "TAK\n";
else cout << "NIE\n";
}
}
return 0;
}
本文作者:bryce蒟蒻的小窝
本文链接:https://www.cnblogs.com/bryceyyds/p/18460870
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步