2025 CSP-S 模拟赛 11
2025 CSP-S 模拟赛 11
得分
| T1 | T2 | T3 | T4 | Sum | Rank |
|---|---|---|---|---|---|
| \(100\) | \(100\) | \(16\) | \(0\) | \(216\) | \(1/20\) |
题解
T1 异或
区域加法问题肯定考虑差分思想,我们将每一次修改差分成这样:
1
1 x
1 x x
x -1 -1 -1
然后斜着做一遍前缀和即可得出最后答案。再利用普通的矩阵加法实现这个操作即可。
#include <bits/stdc++.h>
#define il inline
#define int long long
using namespace std;
const int Maxn = 1e3 + 5;
const int Inf = 2e9;
template <typename T> il void chkmax(T &x, T y) {x = (x >= y ? x : y);}
template <typename T> il void chkmin(T &x, T y) {x = (x <= y ? x : y);}
template <typename T>
il void read(T &x) {
x = 0; char ch = getchar(); bool flg = 0;
for(; ch < '0' || ch > '9'; ch = getchar()) flg = (ch == '-');
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
flg ? x = -x : 0;
}
template <typename T>
il void write(T x, bool typ = 1) {
static short Stk[50], Top = 0;
x < 0 ? putchar('-'), x = -x : 0;
do Stk[++Top] = x % 10, x /= 10; while(x);
while(Top) putchar(Stk[Top--] | 48);
typ ? putchar('\n') : putchar(' ');
}
il void IOS() {ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);}
il void File() {freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);}
bool Beg;
int n, q;
int a[Maxn][Maxn];
il void add(int x1, int y1, int x2, int y2, int val) {
a[x1][y1] += val; a[x1][y2 + 1] -= val; a[x2 + 1][y1] -= val; a[x2 + 1][y2 + 1] += val;
}
bool End;
il void Usd() {cerr << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n"; }
signed main() {
read(n), read(q);
while(q--) {
int r, c, l, s;
read(r), read(c), read(l), read(s);
add(r, c, min(r + l - 1, n), c, s);
if(r + l <= n && c + 1 <= n) add(r + l, c + 1, r + l, min(c + l, n), -s);
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
a[i][j] += a[i][j - 1] + a[i - 1][j] - a[i - 1][j - 1];
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
a[i][j] += a[i - 1][j - 1];
}
}
int ans = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
ans ^= a[i][j];
}
}
write(ans);
Usd();
return 0;
}
T2 游戏
手玩几组数据后会发现一件事:在某些条件下,双方都有能力使得答案为空集,此时答案必定为 \(0\)。我们来探究一下这个条件。
发现每一次操作会分出两个集合,如果我们每一次都选取较小的那个集合,在 \(\log n\) 次后我们就可以得到空集。所以当 \(m>2\log n\) 的时候,即 \(m>28\) 的时候双方都有能力使得答案为 \(0\)。此时我们只需要考虑 \(m\le 28\) 的情况即可。
这个情况就非常简单了,随意写一个爆搜做到 \(O(nm)\) 的复杂度即可。
#include <bits/stdc++.h>
#define il inline
#define int long long
using namespace std;
const int Maxn = 2e5 + 5;
const int Inf = 1e17;
template <typename T> il void chkmax(T &x, T y) {x = (x >= y ? x : y);}
template <typename T> il void chkmin(T &x, T y) {x = (x <= y ? x : y);}
template <typename T>
il void read(T &x) {
x = 0; char ch = getchar(); bool flg = 0;
for(; ch < '0' || ch > '9'; ch = getchar()) flg = (ch == '-');
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
flg ? x = -x : 0;
}
template <typename T>
il void write(T x, bool typ = 1) {
static short Stk[50], Top = 0;
x < 0 ? putchar('-'), x = -x : 0;
do Stk[++Top] = x % 10, x /= 10; while(x);
while(Top) putchar(Stk[Top--] | 48);
typ ? putchar('\n') : putchar(' ');
}
il void IOS() {ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);}
il void File() {freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);}
bool Beg;
int n, m, a[Maxn], b[Maxn];
int t[Maxn];
il int dfs(int x, int l, int r) {
if(l > r) return 0;
if(x == m + 1) {
int sm = 0;
for(int i = l; i <= r; i++) sm += a[i];
return sm;
}
for(int i = l; i <= r; i++) t[i] = a[i];
int cnt = l, mid = 0;
for(int i = l; i <= r; i++) if(t[i] % b[x] == 0) a[cnt++] = t[i];
mid = cnt - 1;
for(int i = l; i <= r; i++) if(t[i] % b[x] != 0) a[cnt++] = t[i];
int res1 = dfs(x + 1, l, mid);
int res2 = dfs(x + 1, mid + 1, r);
if(x & 1) return min(res1, res2);
else return max(res1, res2);
}
bool End;
il void Usd() {cerr << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n"; }
signed main() {
read(n), read(m);
if(m > 28) {write(0); return 0;}
for(int i = 1; i <= n; i++) read(a[i]);
for(int i = 1; i <= m; i++) read(b[i]);
write(dfs(1, 1, n));
Usd();
return 0;
}
T3 连通块
先考虑暴力,我们 \(O(n^2)\) 连边,然后单次 \(O(n)\) 判断删掉一个点后最大连通块大小。总复杂度是 \(O(n^2)\) 的。
考虑分别优化这两个部分。先看连边部分,发现两个点的 \(\gcd\) 为合数说明这两个数是某两个质数乘积的倍数。我们枚举这两个质数,找出所有符合条件的数字,则这些数字应该连成一个完全图。利用经典套路,建立一个虚点向这些数字连边即可。
然后我们来优化后面的部分。我们要删去一个点然后求出剩余连通块大小最大值,容易发现我们删去的一定是当前最大连通块上的一个点,并且有割点要尽可能删去割点。考虑建立圆方树,利用子树信息求出答案即可。
#include <bits/stdc++.h>
#define il inline
#define int long long
using namespace std;
const int Maxn = 3e6 + 5, Maxm = 1e7 + 5;
const int Inf = 2e9;
template <typename T> il void chkmax(T &x, T y) {x = (x >= y ? x : y);}
template <typename T> il void chkmin(T &x, T y) {x = (x <= y ? x : y);}
template <typename T>
il void read(T &x) {
x = 0; char ch = getchar(); bool flg = 0;
for(; ch < '0' || ch > '9'; ch = getchar()) flg = (ch == '-');
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
flg ? x = -x : 0;
}
template <typename T>
il void write(T x, bool typ = 1) {
static short Stk[50], Top = 0;
x < 0 ? putchar('-'), x = -x : 0;
do Stk[++Top] = x % 10, x /= 10; while(x);
while(Top) putchar(Stk[Top--] | 48);
typ ? putchar('\n') : putchar(' ');
}
il void IOS() {ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);}
il void File() {freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);}
bool Beg;
int T;
int n, m, a[Maxn];
int fa[Maxn], sz[Maxn];
int prim[1000005], cnt, vis[Maxm], mnp[Maxm], id[Maxm];
il void init(int n) {
for(int i = 2; i <= n; i++) {
if(!vis[i]) prim[++cnt] = i, mnp[i] = i;
for(int j = 1, x; (x = i * prim[j]) <= n; j++) {
vis[x] = 1;
mnp[x] = prim[j];
if(i % prim[j] == 0) break;
}
}
for(int i = 2; i <= n; i++) if(vis[i] && !vis[i / mnp[i]]) id[i] = ++m;
}
il int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]);}
il void merge(int x, int y) {
x = find(x), y = find(y);
if(x == y) return ;
if(sz[x] > sz[y]) swap(x, y);
sz[y] += sz[x], fa[x] = y;
}
int head[Maxn], edgenum;
struct node {
int nxt, to;
}edge[Maxn];
il void add(int u, int v) {
// cout << u << " " << v << '\n';
edge[++edgenum] = {head[u], v}; head[u] = edgenum;
edge[++edgenum] = {head[v], u}; head[v] = edgenum;
}
int fac[Maxn], num[Maxn];
il void Div(int x, int t) {
int tot = 0;
while(x > 1) {
int w = mnp[x];
fac[++tot] = w; num[tot] = 0;
while(x % w == 0) {
num[tot]++;
x /= w;
}
}
for(int i = 1; i <= tot; i++) {
for(int j = i + (num[i] <= 1); j <= tot; j++) {
if(fac[i] * fac[j] < Maxm) {
int p = id[fac[i] * fac[j]] + n;
add(p, t), merge(t, p);
}
}
}
}
int sum, ans;
int dfn[Maxn], low[Maxn], idx, stk[Maxn], top, siz[Maxn];
il void tarjan(int x, int rt) {
dfn[x] = low[x] = ++idx;
stk[++top] = x;
int cld = 0, flg = 0, ret = 0;
siz[x] = (x <= n);
// cerr << x << " " << rt << '\n';
for(int i = head[x]; i; i = edge[i].nxt) {
int to = edge[i].to;
if(!dfn[to]) {
cld++;
tarjan(to, rt);
low[x] = min(low[x], low[to]);
if(low[to] >= dfn[x]) {
if(x != rt || cld > 1) flg = 1;
int s = 0;
while(1) {
int v = stk[top--];
siz[x] += siz[v]; s += siz[v];
if(v == to) break;
}
chkmax(ret, s);
}
}
else low[x] = min(low[x], dfn[to]);
}
if(x <= n) {
if(flg) chkmin(ans, max(ret, sum - siz[x]));
else chkmin(ans, sum - 1);
}
}
il void init() {
edgenum = idx = top = 0;
for(int i = 1; i <= n + m; i++) head[i] = dfn[i] = low[i] = 0;
}
il void solve() {
init();
read(n);
for(int i = 1; i <= n + m; i++) fa[i] = i, sz[i] = (i <= n);
for(int i = 1; i <= n; i++) read(a[i]);
for(int i = 1; i <= n; i++) Div(a[i], i);
int mx = 0, lmx = 0, pos = 0;
for(int i = 1; i <= n + m; i++) {
if(fa[i] == i) {
if(sz[i] >= mx) lmx = mx, mx = sz[i], pos = i;
else if(sz[i] > lmx) lmx = sz[i];
}
}
ans = sum = mx;
// cout << mx << " " << lmx << " " << pos << '\n';
tarjan(pos, pos);
write(max(lmx, ans));
}
bool End;
il void Usd() {cerr << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n"; }
signed main() {
read(T); init(1e7);
while(T--) solve();
Usd();
return 0;
}
T4 公交路线
考虑对每个点计算有一条路径端点为该点的方案数,总方案数就是总和除以 \(4\),单次询问直接减去对应贡献即可。
考虑一条路径 \((u,v)\) 对 \(u\) 的贡献,我们需要计算另一条路径不与该路径相交的路径数量。直接算并不好算,考虑正难则反。设 \(f(u)\) 表示 \(u\) 子树内经过 \(u\) 的路径总数,\(g(u)\) 表示经过 \(u\) 的所有路径数量。总方案数为 \(\sum f(i)\),而与这条路径相交的路径条数为 \(\sum f(i)+g(\text{LCA}(u,v))\),两者相减即可。
求出 \(f(u)\) 和 \(g(u)\) 是比较容易的,对每种颜色建虚树,转化为树上单点加、链加,离线下来树上差分即可求出。
然后继续考虑计算答案,令 \(F(i)\) 表示 \(i\) 根链上 \(f(i)\) 之和,则答案进一步转化为 \(\sum f(i)-F(u)-F(v)+2F(\text{LCA}(u,v))-g(\text{LCA}(u,v))\)。\(\sum f(i)-F(u)-F(v)\) 是容易求出来的,然后对于后面的部分,再次对每种颜色建虚树,枚举 \(\text{LCA}\),将贡献转化为子树加,再做一次树上差分即可。
如此复杂度即为 \(O(n\log n)\),精细实现可以做到 \(O(n)\)。
#include <bits/stdc++.h>
#define il inline
#define int long long
using namespace std;
const int Maxn = 2e5 + 5;
template <typename T> il void chkmax(T &x, T y) {x = (x >= y ? x : y);}
template <typename T> il void chkmin(T &x, T y) {x = (x <= y ? x : y);}
template <typename T>
il void read(T &x) {
x = 0; char ch = getchar(); bool flg = 0;
for(; ch < '0' || ch > '9'; ch = getchar()) flg = (ch == '-');
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
flg ? x = -x : 0;
}
template <typename T>
il void write(T x, bool typ = 1) {
static short Stk[50], Top = 0;
x < 0 ? putchar('-'), x = -x : 0;
do Stk[++Top] = x % 10, x /= 10; while(x);
while(Top) putchar(Stk[Top--] | 48);
typ ? putchar('\n') : putchar(' ');
}
il void IOS() {ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);}
il void File() {freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);}
bool Beg;
int n, m, k;
int c[Maxn];
int head[Maxn], edgenum;
struct node {
int nxt, to;
}edge[Maxn];
il void add(int u, int v) {
edge[++edgenum] = {head[u], v}; head[u] = edgenum;
edge[++edgenum] = {head[v], u}; head[v] = edgenum;
}
int dfn[Maxn], fa[Maxn], mn[18][Maxn], idx;
namespace T {
int get(int x, int y) {return dfn[x] < dfn[y] ? x : y;}
il void dfs(int x, int fth) {
mn[0][dfn[x] = ++idx] = fa[x] = fth;
for(int i = head[x]; i; i = edge[i].nxt) {
int to = edge[i].to;
if(to == fth) continue;
dfs(to, x);
}
}
il void init() {
for(int i = 1; i <= 17; i++) {
for(int j = 1; j + (1 << i) - 1 <= n; j++) {
mn[i][j] = get(mn[i - 1][j], mn[i - 1][j + (1 << (i - 1))]);
}
}
}
il int lca(int u, int v) {
if(u == v) return u;
if((u = dfn[u]) > (v = dfn[v])) swap(u, v);
int k = __lg(v - u); u++;
return get(mn[k][u], mn[k][v - (1 << k) + 1]);
}
}
int c1[Maxn], c2[Maxn];
int f[Maxn], F[Maxn], g[Maxn];
int sm = 0;
il void dfs(int x) {
f[x] += c1[x]; sm += f[x];
F[x] = F[fa[x]] + f[x];
for(int i = head[x]; i; i = edge[i].nxt) {
int to = edge[i].to;
if(to == fa[x]) continue;
dfs(to); c2[x] += c2[to];
}
g[x] = f[x] + c2[x];
}
int ans[Maxn];
namespace VT {
int head[Maxn], edgenum;
struct node {
int nxt, to;
}edge[Maxn];
il void add(int u, int v) {
edge[++edgenum] = {head[u], v};
head[u] = edgenum;
}
int s[Maxn], top = 0;
il void build(vector <int> &v) {
edgenum = top = 0;
s[++top] = v[0]; head[v[0]] = 0;
for(int i = 1; i < v.size(); i++) {
int x = v[i], t = s[top], l = T::lca(x, t);
if(l == t) {
head[x] = 0; s[++top] = x;
continue;
}
while(dfn[s[top - 1]] > dfn[l]) add(s[top - 1], s[top]), top--;
if(s[top - 1] != l) {
head[l] = 0; add(l, s[top]); s[top] = l;
}
else add(l, s[top--]);
head[x] = 0; s[++top] = x;
}
for(int i = 1; i < top; i++) add(s[i], s[i + 1]);
}
int sum, col, siz[Maxn], f1[Maxn], g1[Maxn];
il void dfs1(int x, int fth) {
siz[x] = (c[x] == col);
f1[x] = g1[x] = 0;
for(int i = head[x]; i; i = edge[i].nxt) {
int to = edge[i].to;
dfs1(to, x);
f1[x] += siz[x] * siz[to];
siz[x] += siz[to];
}
g1[x] = siz[x] * (sum - siz[x]);
c1[x] += f1[x], c2[fth] -= g1[x], c2[x] += g1[x];
}
il void solve1(int cc, vector <int> &v) {
col = cc;
sum = v.size();
build(v); int rt = s[1];
dfs1(rt, 0);
}
int ct[Maxn];
il void dfs2(int x) {
siz[x] = (c[x] == col);
for(int i = head[x]; i; i = edge[i].nxt) {
int to = edge[i].to;
dfs2(to);
siz[x] += siz[to];
}
if(c[x] == col) ans[x] += (2 * F[x] - g[x]) * (siz[x] - 1);
for(int i = head[x]; i; i = edge[i].nxt) {
int to = edge[i].to;
ct[to] += (2 * F[x] - g[x]) * (siz[x] - siz[to]);
}
}
il void dfs3(int x, int w) {
w += ct[x];
if(c[x] == col) ans[x] += w;
ct[x] = 0;
for(int i = head[x]; i; i = edge[i].nxt) {
int to = edge[i].to;
dfs3(to, w);
}
}
il void solve2(int cc, vector <int> &v) {
col = cc;
sum = v.size();
int ret = 0;
for(auto p : v) ret += F[p];
for(auto p : v) ans[p] += (sm - F[p]) * (sum - 1) - (ret - F[p]);
build(v); int rt = s[1];
dfs2(rt); dfs3(rt, 0);
}
}
vector <int> nod[Maxn];
bool End;
il void Usd() {cerr << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n"; }
signed main() {
read(n), read(m), read(k);
for(int i = 1; i <= n; i++) read(c[i]), nod[c[i]].push_back(i);
for(int i = 1, u, v; i < n; i++) {
read(u), read(v);
add(u, v);
}
T::dfs(1, 0), T::init();
for(int i = 1; i <= k; i++) {
if(nod[i].empty()) continue;
sort(nod[i].begin(), nod[i].end(), [](int x, int y){return dfn[x] < dfn[y];});
VT::solve1(i, nod[i]);
}
dfs(1);
for(int i = 1; i <= k; i++) {
if(nod[i].empty()) continue;
VT::solve2(i, nod[i]);
}
int res = 0;
for(int i = 1; i <= n; i++) res += ans[i];
res >>= 2;
write(res);
while(m--) {
int x; read(x);
write(res - ans[x]);
}
Usd();
return 0;
}

浙公网安备 33010602011771号