2025 省选模拟 12
2025 省选模拟 12
得分
| T1 | T2 | T3 | 总分 | 排名 |
|---|---|---|---|---|
| \(55\) | \(32\) | \(35\) | \(122\) | \(4/7\) |
题解
T1 区间
考虑一个事实:对于一个区间 \([l,r]\),如果其合法,那么其和一定是 \(2^x(x\in[A,A+\log n])\),其中 \(A\) 是 \([l,r]\) 中最大的 \(a\)。于是我们可以考虑在笛卡尔树上启发式分裂,先枚举 \(x\),然后枚举较短的一边,用哈希表查询另一个区间是否有合法端点。这样做的复杂度就是 \(O(n\log^2 n)\) 的。
#include <bits/stdc++.h>
#define int long long
#define ll __int128
using namespace std;
const int Maxn = 2e5 + 5;
const int Inf = 2e9;
const int Mod = (int)4e18 - 113;
int n, a[Maxn];
namespace ST {
int _max(int x, int y) {
return a[x] > a[y] ? x : y;
}
int mx[Maxn][19];
void init() {
for(int i = 1; i <= n; i++) mx[i][0] = i;
for(int j = 1; j <= 18; j++) {
for(int i = 1; i + (1 << j) - 1 <= n; i++) {
mx[i][j] = _max(mx[i][j - 1], mx[i + (1 << (j - 1))][j - 1]);
}
}
}
int query(int l, int r) {
int k = __lg(r - l + 1);
return _max(mx[l][k], mx[r - (1 << k) + 1][k]);
}
}
namespace HSH {
static const int P = 1145141;
int head[P], nxt[Maxn], pos[Maxn], val[Maxn], tot;
void ins(int p, int v) {
int id = v % P;
nxt[++tot] = head[id], pos[tot] = p, val[tot] = v;
head[id] = tot;
}
int query(int v, int l, int r) {
int id = v % P, sum = 0;
for(int i = head[id]; i; i = nxt[i]) {
if(l <= pos[i] && pos[i] <= r && val[i] == v) sum++;
}
return sum;
}
}
namespace LSP {
static const int B = 31624;
int pw1[B + 5], pw2[B + 5];
void init() {
pw1[0] = 1;
for(int i = 1; i <= B; i++) pw1[i] = (ll)pw1[i - 1] * 2 % Mod;
pw2[0] = 1;
for(int i = 1; i <= B; i++) pw2[i] = (ll)pw2[i - 1] * pw1[B] % Mod;
}
int qpow(int x) {
int a = x % B, b = x / B;
return (ll)pw1[a] * pw2[b] % Mod;
}
}
int sum[Maxn], ans;
void solve(int l, int r) {
if(l > r) return ;
if(l == r) {ans++; return ;}
int mid = ST::query(l, r), val = a[mid];
solve(l, mid - 1); solve(mid + 1, r);
for(int i = val; i <= val + 20; i++) {
int tar = LSP::qpow(i);
if(mid - l < r - mid) {
for(int j = l; j <= mid; j++) {
int num = (tar + sum[j - 1]) % Mod;
ans += HSH::query(num, mid, r);
}
}
else {
for(int j = mid; j <= r; j++) {
int num = (sum[j] - tar + Mod) % Mod;
ans += HSH::query(num, l - 1, mid - 1);
}
}
}
}
signed main() {
freopen("segment.in", "r", stdin);
freopen("segment.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
ST::init();
LSP::init();
HSH::ins(0, 0);
for(int i = 1; i <= n; i++) {
sum[i] = (sum[i - 1] + LSP::qpow(a[i])) % Mod;
HSH::ins(i, sum[i]);
}
solve(1, n);
cout << ans << '\n';
return 0;
}
T2 圣诞树
考虑到 \(a,b\) 必须经过 \(c\) 实际上就是两个限制:
- \(a,b\) 不能在 \(c\) 的同一个儿子的子树内。
- \(a,b\) 不能同时不在 \(c\) 子树内。
用 \(v(i,j)\) 表示 \(i\) 礼物是否在 \(j\) 子树内,则上述限制可以看作变量间的限制。显然这是一个 2-SAT 问题,直接跑一边即可。
#include <bits/stdc++.h>
using namespace std;
const int Maxn = 2e5 + 5;
const int Maxm = 6.4e7 + 5;
const int Inf = 2e9;
namespace cplx {bool beg;}
int n, m, q;
vector <int> E[Maxn];
int V(int i, int j, int x) {return (i - 1) * n + j + x * n * m;}
int dep[Maxn], fa[Maxn];
void dfs(int x, int fth) {
fa[x] = fth;
dep[x] = dep[fth] + 1;
for(auto to : E[x]) {
if(to == fth) continue;
dfs(to, x);
}
}
int head[Maxn], edgenum;
struct node {
int nxt, to;
}edge[Maxm];
void add(int u, int v) {
edge[++edgenum] = {head[u], v};
head[u] = edgenum;
}
int dfn[Maxn], low[Maxn], ind, scc, st[Maxn], top, ins[Maxn], bel[Maxn];
void tarjan(int x) {
dfn[x] = low[x] = ++ind;
st[++top] = x;
ins[x] = 1;
for(int i = head[x]; i; i = edge[i].nxt) {
int to = edge[i].to;
if(!dfn[to]) {
tarjan(to);
low[x] = min(low[x], low[to]);
}
else if(ins[to]) low[x] = min(low[x], dfn[to]);
}
if(dfn[x] == low[x]) {
scc++;
while(1) {
int v = st[top--];
ins[v] = 0;
bel[v] = scc;
if(v == x) break;
}
}
}
namespace cplx {
bool end;
void mem() {cerr << (&beg - &end) / 1024.0 / 1024.0 << '\n';}
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m >> q;
for(int i = 1; i < n; i++) {
int u, v; cin >> u >> v;
E[u].push_back(v); E[v].push_back(u);
}
dfs(1, 0);
for(int i = 1; i <= m; i++) {
for(int j = 1; j <= n; j++) {
int p = fa[j];
if(!p) continue;
add(V(i, j, 1), V(i, p, 1));
add(V(i, p, 0), V(i, j, 0));
for(auto to : E[p]) {
if(to != j && to != fa[p]) {
add(V(i, j, 1), V(i, to, 0));
}
}
}
add(V(i, 1, 0), V(i, 1, 1));
}
for(int i = 1; i <= q; i++) {
int a, b, c; cin >> a >> b >> c;
for(auto to : E[c]) {
if(to == fa[c]) continue;
add(V(a, to, 1), V(b, to, 0));
add(V(b, to, 1), V(a, to, 0));
}
add(V(a, c, 0), V(b, c, 1));
add(V(b, c, 0), V(a, c, 1));
}
int N = 2 * n * m;
for(int i = 1; i <= N; i++) if(!dfn[i]) tarjan(i);
for(int i = 1; i <= m; i++) {
int pos = 0;
for(int j = 1; j <= n; j++) {
if(bel[V(i, j, 1)] < bel[V(i, j, 0)]) {
if(dep[j] > dep[pos]) pos = j;
}
}
cout << pos << " ";
}
return 0;
}
T3 神奇国度
首先考虑序列上怎么做。我们可以对每一个位置维护一个它上一次被清空的时间。那么从整体上来看我们每一次会覆盖一个区间的标记,显然这是颜色段均摊,用 set 维护一下即可。然后考虑计算答案,不难发现,对于一个能量池 \((s,v)\),当时间间隔为 \(\Delta t\) 时,如果 \(\lceil\tfrac{s}{v}\rceil\le \Delta t\),其贡献为 \(s\);否则为 \(\Delta t\times v\)。我们可以用主席树或者权值线段树分裂合并来求解答案,复杂度是 \(O((n+q)\log n)\) 的。
然后考虑没有 \(s\) 限制时怎样做,不难发现如果我们将 \((dfn,dep)\) 当成坐标放到平面上,每次查询的都是一个矩形内的点,显然用 K-D Tree 直接做即可,复杂度 \(O(q\sqrt n)\)。
然后考虑正解,实际上我们只需要把上面的算法结合起来。显然我们依然需要维护每一个点上一次被清空的时间,而单次修改只会修改 \(O(\sqrt n)\) 个单点和矩形,对于矩形修改我们采用打标记的方式即可。那么在查询的时候,我们需要搜到所有标记都一致的矩形处再统计答案,此时所有点被清空的时间久相等了,依然采用上面的主席树去求答案即可。根据均摊分析,时间复杂度是 \(O(q\sqrt n \log n)\) 的,可以通过。
#include <bits/stdc++.h>
#define int long long
namespace FastIO {
char buf[1 << 20], obuf[1 << 20], *p1 = buf, *p2 = buf, *p3 = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++)
#define flush() (fwrite(obuf, 1, p3 - obuf, stdout))
#define putchar(x) (p3 == obuf + (1 << 20) && (flush(), p3 = obuf), *p3++ = (x))
class Flush{public:~Flush(){flush();}}_;
inline void read(int &x) {
x = 0; bool flg = 0; char ch = getchar();
while(!isdigit(ch)) flg ^= (ch == '-'), ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
flg ? x = -x : x;
}
inline void write(int x, int typ = 1) {
x < 0 ? x = -x, putchar('-') : 0;
static short stack[50], top = 0;
do stack[++top] = x % 10, x /= 10; while(x);
while(top) putchar(stack[top--] | 48);
typ ? putchar('\n') : putchar(' ');
}
}
using namespace FastIO;
using namespace std;
const int Maxn = 2e5 + 5;
const int Inf = 2e9;
int n, q, v[Maxn], s[Maxn], fa[Maxn], d[Maxn];
int mx[Maxn], t[Maxn], tot;
int head[Maxn], edgenum;
struct node {
int nxt, to, w;
}edge[Maxn];
void add(int u, int v, int w) {
edge[++edgenum] = {head[u], v, w};
head[u] = edgenum;
}
int dis[Maxn], dfn[Maxn], ind, siz[Maxn];
void dfs(int x) {
dfn[x] = ++ind;
siz[x] = 1;
for(int i = head[x]; i; i = edge[i].nxt) {
int to = edge[i].to;
dis[to] = dis[x] + edge[i].w;
dfs(to);
siz[x] += siz[to];
}
}
struct Point {
int v[2], id;
}a[Maxn];
int rt;
int ord[Maxn], idx, rnk[Maxn];
namespace KDT {
struct KD_Tree {
int l, r, id, siz, v[2], mn[2], mx[2];
int t, tag;//当前点时间,矩阵内标记
}t[Maxn];
#define ls(p) t[p].l
#define rs(p) t[p].r
int tot = 0;
void pushup(int p) {
t[p].siz = t[ls(p)].siz + t[rs(p)].siz + 1;
for(int i = 0; i < 2; i++) {
t[p].mn[i] = t[p].mx[i] = t[p].v[i];
if(ls(p)) {
t[p].mn[i] = min(t[p].mn[i], t[ls(p)].mn[i]);
t[p].mx[i] = max(t[p].mx[i], t[ls(p)].mx[i]);
}
if(rs(p)) {
t[p].mn[i] = min(t[p].mn[i], t[rs(p)].mn[i]);
t[p].mx[i] = max(t[p].mx[i], t[rs(p)].mx[i]);
}
}
}
void build(int &p, int l, int r, int typ) {
if(l > r) {p = 0; return ;}
int mid = (l + r) >> 1;
nth_element(a + l, a + mid, a + r + 1, [&](Point x, Point y){return x.v[typ] < y.v[typ];});
p = ++tot;
ord[++idx] = p, rnk[p] = idx;
t[p].v[0] = a[mid].v[0], t[p].v[1] = a[mid].v[1], t[p].t = 0, t[p].tag = -1, t[p].id = a[mid].id;
build(ls(p), l, mid - 1, typ ^ 1), build(rs(p), mid + 1, r, typ ^ 1);
pushup(p);
}
#undef ls
#undef rs
}
int rts[Maxn];
namespace SMT {
struct node {
int l, r, sms, smv;
}t[Maxn * 20];
#define ls(p) t[p].l
#define rs(p) t[p].r
int tot = 0;
int mdf(int p, int l, int r, int x, int s, int v) {
int rt = ++tot;
t[rt] = t[p]; t[rt].sms += s, t[rt].smv += v;
if(l == r) return rt;
int mid = (l + r) >> 1;
if(x <= mid) ls(rt) = mdf(ls(p), l, mid, x, s, v);
else rs(rt) = mdf(rs(p), mid + 1, r, x, s, v);
return rt;
}
void query(int p, int q, int l, int r, int pl, int pr, int &ss, int &sv) {
if(pl <= l && r <= pr) {
ss += t[q].sms - t[p].sms;
sv += t[q].smv - t[p].smv;
return ;
}
int mid = (l + r) >> 1;
if(pl <= mid) query(ls(p), ls(q), l, mid, pl, pr, ss, sv);
if(pr > mid) query(rs(p), rs(q), mid + 1, r, pl, pr, ss, sv);
}
#undef ls
#undef rs
}
namespace KDT {
#define ls(p) t[p].l
#define rs(p) t[p].r
void build() {
build(rt, 1, n, 0);
t[rt].tag = 0;
for(int i = 1; i <= idx; i++) {
int id = t[ord[i]].id;
rts[i] = SMT::mdf(rts[i - 1], 0, 1e9, (s[id] - 1) / v[id] + 1, s[id], v[id]);
}
}
void pushtag(int p, int v) {
t[p].t = t[p].tag = v;
}
void pushdown(int p) {
if(t[p].tag != -1) {
if(ls(p)) pushtag(ls(p), t[p].tag);
if(rs(p)) pushtag(rs(p), t[p].tag);
t[p].tag = -1;
}
}
bool checkin(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
return x3 <= x1 && x2 <= x4 && y3 <= y1 && y2 <= y4;
}
bool checkout(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
return x3 > x2 || x4 < x1 || y3 > y2 || y4 < y1;
}
int solve(int p, int tim) {
if(!p) return 0;
if(t[p].tag != -1) {
int num = tim - t[p].tag;
int sum = SMT::t[rts[rnk[p] + t[p].siz - 1]].smv - SMT::t[rts[rnk[p] - 1]].smv;
int ss = 0, sv = 0;
SMT::query(rts[rnk[p] - 1], rts[rnk[p] + t[p].siz - 1], 0, 1e9, 0, num, ss, sv);
int res = ss + (sum - sv) * num;
return res;
}
int res = min((tim - t[p].t) * v[t[p].id], s[t[p].id]);
return res + solve(ls(p), tim) + solve(rs(p), tim);
}
int solve(int p, int x1, int x2, int y1, int y2, int tim) {
if(!p) return 0;
if(checkin(t[p].mn[0], t[p].mn[1], t[p].mx[0], t[p].mx[1], x1, y1, x2, y2)) {
int res = solve(p, tim);
pushtag(p, tim);
return res;
}
if(checkout(t[p].mn[0], t[p].mn[1], t[p].mx[0], t[p].mx[1], x1, y1, x2, y2)) return 0;
pushdown(p);
int res = 0;
if(checkin(t[p].v[0], t[p].v[1], t[p].v[0], t[p].v[1], x1, y1, x2, y2)) {
res += min((tim - t[p].t) * v[t[p].id], s[t[p].id]);
t[p].t = tim;
}
return res + solve(ls(p), x1, x2, y1, y2, tim) + solve(rs(p), x1, x2, y1, y2, tim);
}
#undef ls
#undef rs
}
signed main() {
freopen("country.in", "r", stdin);
freopen("country.out", "w", stdout);
read(n);
for(int i = 1; i <= n; i++) read(v[i]);
for(int i = 1; i <= n; i++) read(s[i]);
for(int i = 2; i <= n; i++) {
read(fa[i]), read(d[i]);
add(fa[i], i, d[i]);
}
dfs(1);
for(int i = 1; i <= n; i++) {
a[i] = {dfn[i], dis[i], i};
}
KDT::build();
read(q);
while(q--) {
int t, x, k; read(t), read(x), read(k);
int ans = KDT::solve(rt, dfn[x], dfn[x] + siz[x] - 1, dis[x], dis[x] + k, t);
write(ans);
}
return 0;
}

浙公网安备 33010602011771号