2026.9 - 做题记录与方法总结
2026.9 - 做题记录与方法总结
2026.9.7
今天军训下雨,暂停一天,爽!
CCF 42th CSP
银行家舍入(rounding)
scanf 控制格式一写就出来了
没啥好说的
#include<bits/stdc++.h>
using namespace std;
int rd() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * w;
}
void wt(int x) {
static int sta[35];
int f = 1;
if(x < 0) f = -1,x *= f;
int top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
if(f == -1) putchar('-');
while (top) putchar(sta[--top] + 48);
}
signed main() {
int n = rd();
vector<int> a1(n + 1),a2(n + 1);
for(int i = 1;i<=n;i++) {
int a,b;
scanf("%d.%d",&a,&b);
if(b < 5) a1[i] = a2[i] = a;
else if(b == 5) {
a1[i] = a + 1;
if(a & 1) a2[i] = a + 1;
else a2[i] = a;
}
else if(b > 5) a1[i] = a2[i] = a + 1;
}
for(int i = 1;i<=n;i++) wt(a1[i]),putchar(' ');
putchar('\n');
for(int i = 1;i<=n;i++) wt(a2[i]),putchar(' ');
return 0;
}
机器人宿管指南(apple)
这种典型的至少最多问题,上二分
#include<bits/stdc++.h>
using namespace std;
#define int long long
int rd() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * w;
}
void wt(int x) {
static int sta[35];
int f = 1;
if(x < 0) f = -1,x *= f;
int top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
if(f == -1) putchar('-');
while (top) putchar(sta[--top] + 48);
}
int n,k,m;
bool check(int o) {
int res = n;bool flag = true;
for(int i = 1;i<=m;i++) {
res -= ceil((res * k) * 1.0 / 100.0);
res -= o;
if(res < 0) flag = false;
}
if(res < 0) flag = false;
return flag;
}
signed main() {
n = rd(),k = rd(),m = rd();
int l = 1,r = 1e9,ans = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid)) l = mid + 1,ans = mid;
else r = mid - 1;
}
wt(ans);
return 0;
}
死锁优化(deadlock)
大模拟,谁爱写谁写。
待补
石子游戏(nim)
区间贪心子问题询问题
首先,根据题目,利用异或前缀和,子问题变成了经典区间最多互不相交线段数
考虑固定 \(l\) 时,由于好段贡献始终是 \(1\),自然贪心,令 \(r\) 越小越好
对于每一个点,可以处理出离他最近的好段结尾,进而定位到以最近好段右侧,即下一段部分开始处
考虑倍增,上面描述的这行,正好可以看作 \(fa[0][i]\) 倍增的初值
对每次询问,从左端点开始,跑倍增,复杂度为 \(\mathcal{O}((n + q)\log n)\)
#include<bits/stdc++.h>
using namespace std;
int rd() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * w;
}
void wt(int x) {
static int sta[35];
int f = 1;
if(x < 0) f = -1,x *= f;
int top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
if(f == -1) putchar('-');
while (top) putchar(sta[--top] + 48);
}
const int N = 1e6+5,INF = N - 1;
const int M = 22;
int n,q,b[N],E[N],O[N],nxt[N];
int fa[M][N];
signed main() {
n = rd(),q = rd();
for(int i = 1;i<=n;i++) b[i] = rd();
for(int i = 1;i<=n;i++) {
if(i & 1) {
if(i >= 3) O[i] = b[i] ^ O[i - 2];
else O[i] = b[i];
E[i] = E[i - 1];
}else {
O[i] = O[i - 1];
E[i] = b[i] ^ E[i - 2];
}
}
unordered_map<int,int> od,ev;
od.reserve(2 * n + 10);ev.reserve(2 * n + 10);
od.max_load_factor(0.7);ev.max_load_factor(0.7);
for(int i = n;i >= 1;i--) {
if(i & 1) {
int cur = O[i];
int c = (i==1 ? 0 : O[i - 2]);
auto it = od.find(c);// 找区间从 i 开始的匹配线段 O[i - 2] {O[i] -> O[it.second]} (因为是 i -> ?,前缀和相同是 (i - 2) 和 ?相同)
int nx = (it == od.end() ? INF : it->second); // 没找到就索引到终止节点
if(cur == c) nx = min(nx,i);// 本身就是 0
nxt[i] = nx;
od[O[i]] = i;
}else {
int cur = E[i];
int c = E[i - 2];
auto it = ev.find(c); //同理
int nx = (it == ev.end() ? INF : it->second);
if(cur == c) nx = min(nx,i);
nxt[i] = nx;
ev[E[i]] = i;
}
}
int LOG = 1;
while((1ll << LOG) <= n + 1) LOG++; // 看最大 2^?
int best = INF;
for(int i = n;i >= 1;i--) {
best = min(best,nxt[i]); // 预处理倍增 , INF 也是终止节点,跳到线段的 **右节点后面**
fa[0][i] = (best > n ? INF: best + 1); //n + 1 是最后一段符合的要求不是终止节点
}
for(int i = 0;i<=LOG;i++) fa[i][n + 1] = INF;
for(int i = 0;i<=LOG;i++) fa[i][INF] = INF;// 防止倍增跑回 0
for(int i = 1;i<=LOG;i++)
for(int j = 1;j<=n;j++)
fa[i][j] = fa[i - 1][fa[i - 1][j]]; // 倍增
while(q--) {
int l = rd(),r = rd();
int ans = 0,pos = l;
for(int i = LOG;i>=0;i--) {
if(fa[i][pos] <= r + 1) // 因为 fa 是记录跑到线段的右节点右侧,所以万一 r 也作为线段的终点,fa 会跑到 r + 1 的位置
ans += (1ll << i),pos = fa[i][pos];
}
wt(ans),putchar('\n');
}
return 0;
}
2026.9.16
P5298 [PKUWC2018] Minimax
好题难想,巧用 \(\operatorname{merge}\) ,
需要求,对 \(rt\) 节点,\(\large \sum\limits_{i = 1}^m i a_i D_i^2\),
\(i,a_i\) 显然好说,关键是 \(D_i\) 很难整,
对一个有两个孩子的节点 \(u \rightarrow ls,rs\),

考虑 \(f_u(v)\) 为在 \(u\) 节点上权值为 \(v\) 的概率。
有
在 \(\operatorname{merge}\) 的时候,会通过递归走到一个区间,而我们的计算需要 \(P(? \geq / \leq v)\) 的概率前缀和/后缀和,
考虑在 \(\operatorname{merge}\) 的同时,记录区间外的左右儿子的线段树的概率前缀后缀和!
如果一边空了,就可以将我们的式子带入计算(没有大小比较了!整个区间在里头,外面的数不是严格大就是严格小!(天才))
int merge(int x,int y,int lx,int rx,int ly,int ry,int p) {
if(!x && !y) return 0;
push_down(x);push_down(y);
if(x && !y) {
addtag(x,(p * ly % mod + (1 - p + mod) * ry % mod) % mod);
return x;
}else if(y && !x) {
addtag(y,(p * lx % mod + (1 - p + mod) * rx % mod) % mod);
return y;
}
int Lx = sum[ls[x]],Rx = sum[rs[x]];
int Ly = sum[ls[y]],Ry = sum[rs[y]];
ls[x] = merge(ls[x],ls[y],lx,(rx + Rx) % mod,ly,(ry + Ry) % mod,p);
rs[x] = merge(rs[x],rs[y],(lx + Lx) % mod,rx,(ly + Ly) % mod,ry,p);
push_up(x);
return x;
}
后面就是很简单的东西了。
AC-code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int rd() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * w;
}
void wt(int x) {
static int sta[35];
int f = 1;
if(x < 0) f = -1,x *= f;
int top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
if(f == -1) putchar('-');
while (top) putchar(sta[--top] + 48);
}
const int mod = 998244353;
int qpow(int x,int k) {
int res = 1;
while(k) {
if(k & 1) (res *= x) %= mod;
(x *= x) %= mod;
k >>= 1;
}
return res;
}
const int N = 3e5+5;
int n,head[N],nxt[N<<1],to[N<<1],a[N],cnt,fa[N],rt[N];
void init() {memset(head,-1,sizeof(head));cnt = 0;}
void add(int u,int v) {
nxt[cnt] = head[u];
to[cnt] = v;
head[u] = cnt++;
}
bitset<N> havson;
namespace sgt{
#define mid ((pl + pr) >> 1)
int ls[N * 30],rs[N * 30],sum[N * 30],mul[N * 30],ans[N * 30],num[N * 30],cnt;
void push_up(int p) {
sum[p] = (sum[ls[p]] + sum[rs[p]]) % mod;
}
int newnode(){
int rs = ++cnt;
mul[cnt] = 1;
return rs;
}
void addtag(int p,int k) {
(sum[p] *= k) %= mod;
(mul[p] *= k) %= mod;
}
void push_down(int p) {
if(mul[p] ^ 1) {
addtag(ls[p],mul[p]);
addtag(rs[p],mul[p]);
mul[p] = 1;
}
}
void update(int &p,int pl,int pr,int k) {
if(!p) p = newnode();
if(pl == pr) {
sum[p] = 1;
return;
}
if(k <= mid) update(ls[p],pl,mid,k);
else update(rs[p],mid + 1,pr,k);
push_up(p);
}
int merge(int x,int y,int lx,int rx,int ly,int ry,int p) {
if(!x && !y) return 0;
push_down(x);push_down(y);
if(x && !y) {
addtag(x,(p * ly % mod + (1 - p + mod) * ry % mod) % mod);
return x;
}else if(y && !x) {
addtag(y,(p * lx % mod + (1 - p + mod) * rx % mod) % mod);
return y;
}
int Lx = sum[ls[x]],Rx = sum[rs[x]];
int Ly = sum[ls[y]],Ry = sum[rs[y]];
ls[x] = merge(ls[x],ls[y],lx,(rx + Rx) % mod,ly,(ry + Ry) % mod,p);
rs[x] = merge(rs[x],rs[y],(lx + Lx) % mod,rx,(ly + Ly) % mod,ry,p);
push_up(x);
return x;
}
#undef mid
}
int top,lsh[N];
void dfs(int x) {
if(!havson[x]) {
sgt::update(rt[x],1,top,a[x]);
return;
}
vector<int> c(2);int cs = 0;
for(int i = head[x];~i;i = nxt[i]) {
int y = to[i];
if(y ^ fa[x]) {
c[cs++] = y;
}
}
if(cs == 1) {
dfs(c[0]);
rt[x] = rt[c[0]];
return;
}
dfs(c[0]);dfs(c[1]);
rt[x] = sgt::merge(rt[c[0]],rt[c[1]],0,0,0,0,a[x]);
}
int d[N];
void _get(int p,int pl,int pr) {
if(pl == pr) {
d[pl] = sgt::sum[p];
return;
}
sgt::push_down(p);
_get(sgt::ls[p],pl,((pl + pr) >> 1));
_get(sgt::rs[p],((pl + pr) >> 1) + 1,pr);
}
signed main() {
n = rd();init();
for(int i = 1;i<=n;i++) {
fa[i] = rd();
havson[fa[i]] = true;
add(fa[i],i);
add(i,fa[i]);
}
int c = qpow(10000,mod - 2);
for(int i = 1;i<=n;i++) {
a[i] = rd();
if(havson[i]) a[i] = a[i] * c % mod;
else lsh[++top] = a[i];
}
sort(lsh + 1,lsh + top + 1);
for(int i = 1;i<=n;i++)
if(!havson[i])
a[i] = lower_bound(lsh + 1,lsh + top + 1,a[i]) - lsh;
dfs(1);_get(rt[1],1,top);
int ans = 0;
for(int i = 1;i<=top;i++)
(ans += i * lsh[i] % mod * d[i] % mod * d[i] % mod) %= mod;
wt(ans);
return 0;
}
HDU 7435 树
线段树合并!
假设 \(A_u \geq A_v\),那么 \(f(u,v) = A_u(A_u - A_v) = A_u^2 - A_uA_v\)
假设只有一个线段树,叶子节点有值,如何计算这些数的 \(ans\) ?
对于一个节点 \(u\) 有 \(ls[u],rs[u]\),首先有 \(ans_u \leftarrow^+ ans_{ls[u]} + ans_{rs[u]}\)
因为是权值线段树,所有右子树的权值一定大于左子树!!!
然后要跨区间,有 \(ans_u \leftarrow^+ num[ls[u]] \times sqsum[rs[u]]\)
然后根据公式减去 \(A_uA_v\),即
现在需要一点一点从底向上爬,边爬边加入,记得跨区间的答案需要翻倍!
AC-code:
#include<bits/stdc++.h>
using namespace std;
int rd() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * w;
}
#define int unsigned long long
void wt(int x) {
static int sta[35];
int f = 1;
if(x < 0) f = -1,x *= f;
int top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
if(f == -1) putchar('-');
while (top) putchar(sta[--top] + 48);
}
#undef int
const int N = 5e5+5;
int n,head[N],nxt[N<<1],to[N<<1],cnt;
void init() {memset(head,-1,sizeof(head));cnt = 0;}
void add(int u,int v) {
nxt[cnt] = head[u];
to[cnt] = v;
head[u] = cnt++;
}
int rt[N],a[N];
unsigned long long ans;
namespace sgt{
const int ND = N * 50;
#define mid ((pl + pr) >> 1)
int ls[ND],rs[ND],cnt;
int s0[ND];
unsigned long long s1[ND];
unsigned long long s2[ND],ans[ND];
void push_up(int p) {
s0[p] = s0[ls[p]] + s0[rs[p]];
s1[p] = s1[ls[p]] + s1[rs[p]];
s2[p] = s2[ls[p]] + s2[rs[p]];
ans[p] = ans[ls[p]] + ans[rs[p]] + ((unsigned long long)s0[ls[p]] * s2[rs[p]] - s1[ls[p]] * s1[rs[p]]) * 2;
}
void update(int &p,int pl,int pr,int k) {
if(!p) p = ++cnt;
if(pl == pr) {
s0[p]++;
s1[p] += k;
s2[p] += 1ull * k * k;
ans[p] = 0;
return;
}
if(k <= mid) update(ls[p],pl,mid,k);
else update(rs[p],mid+1,pr,k);
push_up(p);
}
int merge(int x,int y,int pl,int pr) {
if(!x || !y) return x | y;
if(pl == pr) {
s0[x] += s0[y];
s1[x] += s1[y];
s2[x] += s2[y];
ans[x] += ans[y];
return x;
}
ls[x] = merge(ls[x],ls[y],pl,mid);
rs[x] = merge(rs[x],rs[y],mid+1,pr);
push_up(x);
return x;
}
#undef mid
}
void dfs(int x,int fa) {
sgt::update(rt[x],1,1e6,a[x]);
for(int i = head[x];~i;i = nxt[i]) {
int y = to[i];
if(y ^ fa) {
dfs(y,x);
rt[x] = sgt::merge(rt[x],rt[y],1,1e6);
}
}
ans ^= sgt::ans[rt[x]];
}
signed main() {
n = rd();init();
for(int i = 1;i<n;i++) {
int u = rd(),v = rd();
add(u,v);add(v,u);
}
for(int i = 1;i<=n;i++) a[i] = rd();
dfs(1,0);
wt(ans);
return 0;
}

浙公网安备 33010602011771号