CSP-S 模拟赛35
T1
其实是傻逼题。常见的套路是枚举右端点,动态维护左端点的贡献。发现右端点移动一位只会对一种颜色有影响,那么考虑线段树维护区间的答案,区间加减每个颜色即时的贡献即可。
代码:
#include <bits/stdc++.h>
#define N 1000005
#define M 8005
#define int long long
using namespace std;
int n, m;
int c[N], d[M];
int lst[N];
int ps[M];
struct Node {
int l, r;
int mx;
int fg;
} e[N << 2];
#define l(i) e[i].l
#define r(i) e[i].r
#define mx(i) e[i].mx
#define fg(i) e[i].fg
#define lc (p << 1)
#define rc (lc | 1)
void push_up(int p) {
mx(p) = max(mx(lc), mx(rc));
}
void build(int p, int l, int r) {
l(p) = l, r(p) = r;
if (l == r)
return;
int mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
}
void push_down(int p) {
if (fg(p) == 0)
return;
fg(lc) += fg(p);
fg(rc) += fg(p);
mx(lc) += fg(p);
mx(rc) += fg(p);
fg(p) = 0;
}
void update(int p, int l, int r, int vl) {
if (l > r || l > r(p) || l(p) > r)
return;
if (l <= l(p) && r(p) <= r) {
mx(p) += vl;
fg(p) += vl;
return;
}
push_down(p);
update(lc, l, r, vl);
update(rc, l, r, vl);
push_up(p);
}
int ans;
signed main() {
freopen("flower.in", "r", stdin);
freopen("flower.out", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
scanf("%lld", &c[i]);
lst[i] = ps[c[i]];
ps[c[i]] = i;
}
for (int i = 1; i <= m; i++)
scanf("%lld", &d[i]);
build(1, 1, n);
for (int i = 1; i <= n; i++) {
update(1, lst[i] + 1, i, d[c[i]]);
if (lst[i])
update(1, lst[lst[i]] + 1, lst[i], -d[c[i]]);
ans = max(ans, mx(1));
}
cout << ans << "\n";
return 0;
}
T2
类似曼哈顿距离的坐标系上问题问题容易考虑曼哈顿转切比雪夫。\((x,y)\rightarrow (x+y,x-y)\) 后贡献变成了 \(\min(|x_1-x_2|,|y_1-y_2|)\)。考虑最优的贡献一定是某两个 \(\neq0\) 的直接距离,那么并查集对距离为 \(0\) 的缩点,然后分别按 \(x,y\) 排序维护最小值再查方案数即可,方案数是并查集 \(siz\) 的乘积。
代码:
#include <bits/stdc++.h>
#define N 100005
using namespace std;
int n, ans;
struct Node {
int x, y, id;
} e[N];
bool cmpx(Node a, Node b) {
return a.x < b.x;
}
bool cmpy(Node a, Node b) {
return a.y < b.y;
}
int fa[N];
int fnd(int x) {
if (x == fa[x])
return x;
return fa[x] = fnd(fa[x]);
}
int siz[N];
void mge(int x, int y) {
x = fnd(x), y = fnd(y);
if (x != y)
fa[y] = x, siz[x] += siz[y];
}
pair<int, int>t[N << 1];
map<pair<int, int>, int>mp;
int tot;
int main() {
freopen("sky.in", "r", stdin);
freopen("sky.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++) {
int x, y;
scanf("%d%d", &x, &y);
e[i].x = x + y;
e[i].y = x - y;
e[i].id = i;
fa[i] = i;
siz[i] = 1;
}
sort(e + 1, e + 1 + n, cmpx);
for (int i = 1; i < n; i++)
if (e[i].x == e[i + 1].x)
mge(e[i].id, e[i + 1].id);
sort(e + 1, e + 1 + n, cmpy);
for (int i = 1; i < n; i++)
if (e[i].y == e[i + 1].y)
mge(e[i].id, e[i + 1].id);
int ds = 0x3f3f3f3f;
for (int i = 1; i < n; i++)
if (fnd(e[i].id) != fnd(e[i + 1].id))
ds = min(ds, e[i + 1].y - e[i].y);
sort(e + 1, e + 1 + n, cmpx);
for (int i = 1; i < n; i++)
if (fnd(e[i].id) != fnd(e[i + 1].id))
ds = min(ds, e[i + 1].x - e[i].x);
for (int i = 1; i < n; i++)
if (fnd(e[i].id) != fnd(e[i + 1].id) && e[i + 1].x - e[i].x == ds)
t[++tot] = make_pair(min(e[i].id, e[i + 1].id), max(e[i].id, e[i + 1].id));
sort(e + 1, e + 1 + n, cmpy);
for (int i = 1; i < n; i++)
if (fnd(e[i].id) != fnd(e[i + 1].id) && e[i + 1].y - e[i].y == ds)
t[++tot] = make_pair(min(e[i].id, e[i + 1].id), max(e[i].id, e[i + 1].id));
sort(t + 1, t + 1 + tot);
tot = unique(t + 1, t + 1 + tot) - t - 1;
for (int i = 1; i <= tot; i++) {
int x = t[i].first, y = t[i].second;
x = fnd(x), y = fnd(y);
if (mp.count(make_pair(x, y)) || mp.count(make_pair(y, x)))
continue;
mp[make_pair(x, y)] = 1;
mp[make_pair(y, x)] = 1;
ans += siz[x] * siz[y];
}
cout << ds << "\n" << ans << "\n";
return 0;
}
T3
考虑每个时刻每个位置上的 \(1\) 能否移动,能走记为 \(1\),不能走记为 \(0\)。那么考虑时间 \(t,t+1\) 之间如何转移。
如果两个 \(1\) 是相邻的,那么第 \(i\) 个 \(1\) 在 \(t\) 时刻可以走一步,\(i+1\) 个 \(1\) 就可以在 \(t+1\) 时刻走一步。也就是说将原串右移一位并在开头补上 \(0\)。
不相邻的情形是两个 \(1\) 相隔 \(k\) 个 \(0\) 时,字符串上的前 \(k\) 个 \(0\) 就变为 \(1\)。那么考虑维护所有 \(0\) 的相对位置,对 "参照系" 的左移相当于右移了字符串,在开头结尾增删可以方便地用 std::deque
维护,第一问的答案就是考虑每个序列 \(1\) 左移到的地点。
对于第二问,考虑每个字符串中每个 \(1\) 的位置 \(p_i\) 意味着 \(p_i\) 时刻之后对逆序对的贡献 \(+1\),而这个贡献是一段区间,差分即可。
#include <bits/stdc++.h>
#define N 5000006
#define int long long
#define mod 998244353
using namespace std;
int t;
int n;
int a[N];
string s;
int pos[N], cnt;
deque<int>q;
int ans[N];
int sum[N];
int sm, res;
int qpow(int x, int y) {
int ans = 1;
while (y) {
if (y & 1)
ans = ans * x % mod;
x = x * x % mod;
y >>= 1;
}
return ans;
}
signed main() {
freopen("chuan.in", "r", stdin);
freopen("chuan.out", "w", stdout);
cin >> t;
cin >> s;
n = s.size();
s = " " + s;
for (int i = 1; i <= n; i++) {
a[i] = s[i] - '0';
if (a[i])
pos[++cnt] = i;
else
sm += cnt;
}
for (int i = 1; i <= t; i++)
q.push_back(i);
for (int i = 1; i <= cnt; i++) {
while (q.size() && q.back() + i > t)
q.pop_back();
q.push_front(1 - i);
for (int j = 1; j < pos[i] - pos[i - 1] && q.size(); j++) {
++sum[q.front() + i];
--sum[min(q.front() + cnt + 1, t + 1)];
q.pop_front();
}
ans[pos[i] - t + q.size()] = 1;
}
for (int i = 0; i <= t; i++) {
if (i > 0)
sum[i] = (sum[i] + sum[i - 1]) % mod;
sm = (sm + sum[i]) % mod;
res ^= (qpow(233, i) * sm % mod);
}
for (int i = 1; i <= n; i++)
cout << ans[i];
puts("");
cout << res << "\n";
return 0;
}
T4
平方题拆开平方,考虑边 \((u,fa_u)\) 的贡献。
对于一条边平方的部分,贡献是 \(siz_u(n-siz_u)\)。
对于两条边的乘积:
- \(v\) 在 \(u\) 子树内,贡献是 \(2w_u\times w_v\times (n-siz_u)\times siz_v\)。
- \(v\) 是 \(u\) 祖先,贡献是 \(2w_u\times w_v\times (n-siz_v)\times siz_u\)。
- 否则贡献是 \(2w_u\times w_v\times siz_u\times siz_v\)。
对于每个 \(u\),考虑所有 \(v\) 的贡献。维护和 \(v\) 相关的 \(siz_v\times w_v\) 和 \(w_v\) 即可。线段树/树状数组。
代码:
#include <bits/stdc++.h>
#define N 100005
#define int long long
#define mod 1000000007
using namespace std;
int n;
int val[N];
int s1[N], s2[N];
int smt, f[N];
namespace Mp {
struct Node {
int to, nxt;
} e[N << 1];
int head[N], cnt;
void add(int u, int v) {
e[++cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
int dfn[N], tot, rk[N];
int siz[N];
void dfs1(int x) {
siz[x] = 1;
for (int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
dfs1(y);
siz[x] += siz[y];
}
}
void dfs2(int x) {
dfn[x] = ++tot;
rk[tot] = x;
s1[x] = (s1[f[x]] + siz[x] * val[x] % mod) % mod;
s2[x] = (s2[f[x]] + val[x]) % mod;
for (int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
dfs2(y);
}
}
}
namespace Seg {
struct Node {
int l, r;
int s1, f1, s2, f2;
} e[N << 2];
#define l(i) e[i].l
#define r(i) e[i].r
#define s1(i) e[i].s1
#define f1(i) e[i].f1
#define s2(i) e[i].s2
#define f2(i) e[i].f2
#define lc (p << 1)
#define rc (lc | 1)
void push_up(int p) {
s1(p) = (s1(lc) + s1(rc)) % mod;
s2(p) = (s2(lc) + s2(rc)) % mod;
}
void build(int p, int l, int r) {
l(p) = l, r(p) = r;
if (l == r) {
s1(p) = s1[Mp::rk[l]];
s2(p) = s2[Mp::rk[l]];
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
push_up(p);
}
void push_down(int p) {
if (f1(p)) {
f1(lc) = (f1(lc) + f1(p)) % mod;
f1(rc) = (f1(rc) + f1(p)) % mod;
s1(lc) = (s1(lc) + f1(p) * (r(lc) - l(lc) + 1) % mod) % mod;
s1(rc) = (s1(rc) + f1(p) * (r(rc) - l(rc) + 1) % mod) % mod;
}
if (f2(p)) {
f2(lc) = (f2(lc) + f2(p)) % mod;
f2(rc) = (f2(rc) + f2(p)) % mod;
s2(lc) = (s2(lc) + f2(p) * (r(lc) - l(lc) + 1) % mod) % mod;
s2(rc) = (s2(rc) + f2(p) * (r(rc) - l(rc) + 1) % mod) % mod;
}
f1(p) = f2(p) = 0;
}
void update(int p, int l, int r, int v1, int v2) {
if (l > r || l > r(p) || l(p) > r)
return;
if (l <= l(p) && r(p) <= r) {
s1(p) = (s1(p) + v1 * (r(p) - l(p) + 1) % mod) % mod;
s2(p) = (s2(p) + v2 * (r(p) - l(p) + 1) % mod) % mod;
f1(p) = (f1(p) + v1) % mod;
f2(p) = (f2(p) + v2) % mod;
return;
}
push_down(p);
update(lc, l, r, v1, v2);
update(rc, l, r, v1, v2);
push_up(p);
}
int query1(int p, int x) {
if (l(p) == r(p) && l(p) == x)
return s1(p);
push_down(p);
int mid = (l(p) + r(p)) >> 1;
if (x <= mid)
return query1(lc, x);
return query1(rc, x);
}
int query2(int p, int x) {
if (l(p) == r(p) && l(p) == x)
return s2(p);
push_down(p);
int mid = (l(p) + r(p)) >> 1;
if (x <= mid)
return query2(lc, x);
return query2(rc, x);
}
}
namespace BIT {
int tree[N];
int lowbit(int x) {
return x & (-x);
}
void add(int x, int v) {
while (x <= n) {
tree[x] += v;
tree[x] %= mod;
x += lowbit(x);
}
}
int ask(int x) {
int ans = 0;
while (x) {
ans += tree[x];
ans %= mod;
x -= lowbit(x);
}
return ans;
}
}
int query(int x, int w) {
int tmp = 0, sz = 0;
sz = (BIT::ask(Mp::dfn[x] + Mp::siz[x] - 1) - BIT::ask(Mp::dfn[x]) + mod) % mod;
int ans = 2 * w % mod * (n - Mp::siz[x]) % mod * sz % mod;
int sv = Seg::query1(1, Mp::dfn[f[x]]);
tmp = (tmp + Seg::query2(1, Mp::dfn[f[x]]) * n % mod - sv + mod) % mod;
tmp = ((tmp + smt - sv - sz - Mp::siz[x] * val[x] % mod) % mod + mod) % mod;
ans = (ans + 2 * tmp % mod * Mp::siz[x] % mod * w % mod) % mod;
return ans;
}
void update(int x, int w) {
Seg::update(1, Mp::dfn[x], Mp::dfn[x] + Mp::siz[x] - 1, Mp::siz[x] * w % mod, w);
BIT::add(Mp::dfn[x], Mp::siz[x] * w % mod);
smt = (smt + Mp::siz[x] * w % mod) % mod;
val[x] = (val[x] + w) % mod;
}
int qpow(int x, int y) {
int ans = 1;
while (y) {
if (y & 1)
ans = ans * x % mod;
x = x * x % mod;
y >>= 1;
}
return ans;
}
int ans, T;
signed main() {
freopen("revive.in", "r", stdin);
freopen("revive.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
cin >> n >> T;
for (int i = 2; i <= n; i++) {
cin >> f[i] >> val[i];
Mp::add(f[i], i);
}
Mp::dfs1(1);
Mp::dfs2(1);
Seg::build(1, 1, n);
for (int i = 1; i <= n; i++) {
smt = (smt + Mp::siz[i] * val[i] % mod) % mod;
BIT::add(Mp::dfn[i], Mp::siz[i] * val[i] % mod);
}
for (int i = 2; i <= n; i++)
ans = (ans + query(i, val[i])) % mod;
ans = ans * qpow(2, mod - 2) % mod;
for (int i = 2; i <= n; i++)
ans = (ans + val[i] * val[i] % mod * Mp::siz[i] % mod * (n - Mp::siz[i]) % mod) % mod;
cout << ans << "\n";
while (T--) {
int x, w;
cin >> x >> w;
ans = (ans - val[x] * val[x] % mod * Mp::siz[x] % mod * (n - Mp::siz[x]) % mod + mod) % mod;
ans = (ans + query(x, w)) % mod;
update(x, w);
ans = (ans + val[x] * val[x] % mod * Mp::siz[x] % mod * (n - Mp::siz[x]) % mod) % mod;
cout << ans << "\n";
}
return 0;
}
标签:lc,int,siz,ans,35,rc,CSP,模拟,mod
From: https://www.cnblogs.com/Rock-N-Roll/p/18459291