Loading

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\)

img

考虑 \(f_u(v)\) 为在 \(u\) 节点上权值为 \(v\) 的概率。

\[f_u(v) = f_{ls}(v) [p \cdot P(R \leq v) + (1 - p) \cdot P(R \geq v)] + \\ f_{rs}(v) [p \cdot P(L \leq v) + (1 - p) \cdot P(L \geq 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\),即

\[ans_u \leftarrow^- sum[ls[u]] \times sum[rs[u]] \]

现在需要一点一点从底向上爬,边爬边加入,记得跨区间的答案需要翻倍!

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;
}
posted @ 2026-09-07 23:47  MingJunYi  阅读(33)  评论(0)    收藏  举报