【香蕉OI】11.12 模拟赛

今天做的太差了,全都写一写。

T1

题意

一棵有根树,每个点有一个颜色。离线询问,每次询问一个子树中第 k 大的颜色。

空间 16 MB。

思路

卡空间的题。主席树显然不行,但是 dsu on tree 却可以。

没有难度,没想到 dsu on tree 是我菜。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = N<<1, Q = 3e5 + 10, inf = (1<<31)-1;
int n, m, vn, c[N];
vector<int> vec;
int h[N], ecnt, nxt[M], v[M];
struct node{
	int x, k, id;
}q[Q];
int beg[N], siz[N], son[N], t[N<<2], ans[Q];

template<class T>inline void read(T &x){
	x = 0; bool fl = 0; char c = getchar();
	while (!isdigit(c)){if (c == '-') fl = 1; c = getchar();}
	while (isdigit(c)){x = (x<<3)+(x<<1)+c-'0'; c = getchar();}
	if (fl) x = -x;
}

void _add(int x, int y){
	nxt[++ecnt] = h[x]; v[ecnt] = y;
	h[x] = ecnt;
}

bool cmp(node x, node y){
	return x.x < y.x;
}

void dfs1(int u, int fa){
	siz[u] = 1;
	for (int i = h[u]; i; i = nxt[i])
		if (v[i] != fa){
			dfs1(v[i], u);
			siz[u] += siz[v[i]];
			if (siz[v[i]] > siz[son[u]]) son[u] = v[i];
		}
}

#define ls (u<<1)
#define rs (u<<1^1)
#define mid (l+r>>1)
void push_up(int u){
	t[u] = t[ls]+t[rs];
}

void modify(int u, int l, int r, int P, int X){
	if (l == r){
		t[u] += X;
		return;
	}
	if (P <= mid) modify(ls, l, mid, P, X);
	else modify(rs, mid+1, r, P, X);
	push_up(u);
}

int query(int u, int l, int r, int K){
	if (l == r) return l;
	if (t[ls] >= K) return query(ls, l, mid, K);
	else return query(rs, mid+1, r, K-t[ls]);
}

void add(int u, int fa, int X){
	modify(1, 1, vn, c[u], X);
	for (int i = h[u]; i; i = nxt[i])
		if (v[i] != fa)
			add(v[i], u, X);
}

void dfs(int u, int fa, bool opt){
	for (int i = h[u]; i; i = nxt[i])
		if (v[i] != fa && v[i] != son[u])
			dfs(v[i], u, 1);
	if (son[u]) dfs(son[u], u, 0);
	for (int i = h[u]; i; i = nxt[i])
		if (v[i] != fa && v[i] != son[u])
			add(v[i], u, 1);
	modify(1, 1, vn, c[u], 1);
	if (beg[u])
		for (int i = beg[u]; i <= m && q[i].x == u; ++ i)
			ans[q[i].id] = vec[query(1, 1, vn, q[i].k)-1];
	if (opt) add(u, fa, -1);
}

int main()
{
	freopen("data.in", "r", stdin); freopen("data.out", "w", stdout);
	read(n);
	for (int i = 1; i <= n; ++ i){
		read(c[i]);
		vec.push_back(c[i]);
	}
	sort(vec.begin(), vec.end());
	vec.resize(unique(vec.begin(), vec.end())-vec.begin());
	vn = vec.size();
	for (int i = 1; i <= n; ++ i)
		c[i] = lower_bound(vec.begin(), vec.end(), c[i])-vec.begin()+1;
	ecnt = 1;
	for (int i = 1; i < n; ++ i){
		int x, y;
		read(x); read(y);
		_add(x, y); _add(y, x);
	}
	read(m);
	for (int i = 1; i <= m; ++ i){
		read(q[i].x); read(q[i].k); q[i].id = i;
	}
	sort(q + 1, q + m + 1, cmp);
	for (int i = m; i >= 1; -- i)
		beg[q[i].x] = i;
	dfs1(1, 0);
	dfs(1, 0, 0);
	for (int i = 1; i <= m; ++ i)
		printf("%d\n", ans[i]);
	return 0;
}

T2

题意

这道题分三个部分:

  1. n 个变量,每个的值都是在 [x,y] 内等概率随机的整数。求这 n 个变量的最大值的期望。
    n ≤ 1 0 9 , x , y ≤ 1 0 5 n\le 10^9,x,y\le 10^5 n109,x,y105

  2. n 个变量,每个的值都是在 [x,y] 内等概率随机的整数。求这 n 个变量的最大值的期望。
    n ≤ 1000 , x , y ≤ 1 0 18 n\le 1000,x,y\le 10^{18} n1000,x,y1018

  3. n 个变量,每个的值都是在 [x,y] 内等概率随机的实数。求这 n 个变量的最大值的期望。
    n , x , y ≤ 1 0 18 n,x,y\le 10^{18} n,x,y1018

思路

第一个题很好推式子。首先我们可以知道最大值为 i i i 的概率是 i n − ( i − 1 ) n / ( y − x + 1 ) n i^n-(i-1)^n/(y-x+1)^n in(i1)n/(yx+1)n ,算是一个容斥。概率乘上权值就是期望,乘完之后大部分都消掉了,留下了这个式子:

a n s = y − ∑ i = 1 y − x i n ( y − x + 1 ) n ans=y-\frac{\sum_{i=1}^{y-x}i^n}{(y-x+1)^n} ans=y(yx+1)ni=1yxin

第一题就拿着这个式子暴力算就好了。

然后是第二题,第二题的 n n n 比较小。考虑如何让复杂度与 x , y x,y x,y 无关。我们有

( p + 1 ) k + 1 − 1 = ∑ i = 0 k ∑ j = 1 p C k + 1 i ∗ j i (p+1)^{k+1}-1=\sum_{i=0}^{k}\sum_{j=1}^{p}C_{k+1}^{i}*j^i (p+1)k+11=i=0kj=1pCk+1iji

换一下顺序

( p + 1 ) k + 1 − 1 = ∑ i = 0 k C k + 1 i ∑ j = 1 p ∗ j i (p+1)^{k+1}-1=\sum_{i=0}^{k}C_{k+1}^{i}\sum_{j=1}^{p}*j^i (p+1)k+11=i=0kCk+1ij=1pji

发现可以用这个式子 O ( n 2 ) O(n^2) O(n2) 递推 n n n 次方前缀和。那么第二题就做完了。

第三题具体推导用微积分,出题人小学三年级就学过微积分了,但是我高二还一无所知,所以感性理解一下,问题就是在一个长度为 1 的线段上切 n 刀求最右边的那段的期望长度。

这个可以看做在一个圆上切 n+1 刀,取出其中一段的期望。显然每段的期望长度是一样的,所以就是 1 n + 1 \frac{1}{n+1} n+11

前两个问题推式子还是挺好推的。但是我只写出了第一个而没有看到 n 小于等于 1000 去想第二个式子。也是对这种 n 次方前缀和的处理方法的不熟悉。而且第二个题还可以用第二类斯特林数做,也是 O ( n 2 ) O(n^2) O(n2) 的。

第三个结论背下来就好了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
int typ;
LL n, x, y;

template<class T>inline void read(T &x){
	x = 0; bool fl = 0; char c = getchar();
	while (!isdigit(c)){if (c == '-') fl = 1; c = getchar();}
	while (isdigit(c)){x = (x<<3)+(x<<1)+c-'0'; c = getchar();}
	if (fl) x = -x;
}

int add(int &x, int y){x += y; if (x >= mod) x -= mod;}

int fpow(LL x, LL y){
	x %= mod;
	int ret = 1;
	while (y){
		if (y & 1) ret = 1LL*ret*x%mod;
		x = 1LL*x*x%mod;
		y >>= 1;
	}
	return ret;
}

int inv(LL x){
	return fpow(x, mod-2);
}

namespace Solver1
{
	void main(){
		int ans = 0;
		for (int i = 1; i <= y-x; ++ i)
			add(ans,fpow(i, n));
		ans = 1LL*ans*inv(fpow(y-x+1, n))%mod;
		ans = (y-ans+mod)%mod;
		printf("%d\n", ans);
		exit(0);
	}
}

namespace Solver2
{
	const int N = 1000 + 10;
	int fac[N], ifac[N], sum[N], ans;
	void init(){
		fac[0] = 1;
		for (int i = 1; i < N; ++ i)
			fac[i] = 1LL*fac[i-1]*i%mod;
		ifac[N-1] = inv(fac[N-1]);
		for (int i = N-2; i >= 0; -- i)
			ifac[i] = 1LL*ifac[i+1]*(i+1)%mod;
	}
	int C(int x, int y){
		return 1LL*fac[x]*ifac[y]%mod*ifac[x-y]%mod;
	}
	void main(){
		init();
		sum[0] = (y-x)%mod;
		for (int k = 1; k <= n; ++ k){
			sum[k] = fpow((y-x+1)%mod, k+1)-1;
			for (int i = 0; i < k; ++ i)
				add(sum[k], mod-1LL*C(k+1, i)*sum[i]%mod);
			sum[k] = 1LL*sum[k]*inv(k+1)%mod;
		}
		ans = (y-1LL*sum[n]*inv(fpow((y-x+1)%mod, n))%mod+mod)%mod;
		printf("%d\n", ans);
		exit(0);
	}
}

namespace Solver3
{
	void main(){
		int ans = (y-1LL*(y-x)%mod*inv(n+1)%mod+mod)%mod;
		printf("%d\n", ans);
	}
}

int main()
{
	freopen("freshmen.in", "r", stdin); freopen("freshmen.out", "w", stdout);
	read(typ); read(n); read(x); read(y);
	if (typ == 1 && y-x <= 1e5) Solver1::main();
	else if (typ == 1 && n <= 1000) Solver2::main();
	else if (typ == 2) Solver3::main();
	return 0;
}

T3

题意

有一个序列 { a i } \{a_i\} {ai},每个数 a i ∈ { 1 , 2 , 3 } a_i\in\{1,2,3\} ai{1,2,3} 。有以下两种操作:

  1. 将一个区间 $[l,r] $内的所有数修改为 x x x
  2. 在区间 [ l , r ] [l,r] [l,r] 内选若干个位置 { p 1 . . . p m } \{p_1...p_m\} {p1...pm},满足 ∀ i < m , p i < p i + 1 , a p i ≤ a p i + 1 \forall i<m,p_i<p_{i+1},a_{p_i}\le a_{p_{i+1}} i<m,pi<pi+1,apiapi+1 ,然后用恰好 k k k 种颜色的旗子(每种颜色可以用任意多个)把这些位置插满。询问这样的插旗方案数。

对于询问, k ≤ 5 k\le 5 k5

思路

题目说这是一道送分题。出题人倒是良心,数据里 70 分都是暴力分。然而却没送到我手里,我题意看挂了,然后又没看到第二个样例(雾)。话说第二个样例就在 pdf 的第二页,看数据范围的时候肯定是要看到的呀 qwq

首先可以想出暴力 DP 。 f i , j , k f_{i,j,k} fi,j,k 表示前 i i i 个位置,已经插了 j j j 种颜色的旗子,结尾的 a i = k a_i=k ai=k 的方案数。

然后感觉可以用矩阵优化,就像动态 DP 那样。但是复杂度不是很对劲。

考虑换一个方式 DP 。

假如有一个方案,插了 s s s 个旗子,那么他的贡献为 ∑ i = 1 k ( − 1 ) k − i ⋅ C k i ⋅ i s \sum_{i=1}^{k}(-1)^{k-i}\cdot C_{k}^{i}\cdot i^s i=1k(1)kiCkiis

那么所有方案的答案就是

∑ s ∈ S ∑ i = 1 k ( − 1 ) k − i ⋅ C k i ⋅ i s \sum_{s\in S}\sum_{i=1}^{k}(-1)^{k-i}\cdot C_{k}^{i}\cdot i^s sSi=1k(1)kiCkiis

换个顺序

∑ i = 1 k ( − 1 ) k − i ⋅ C k i ⋅ ∑ s ∈ S i s \sum_{i=1}^{k}(-1)^{k-i}\cdot C_{k}^{i}\cdot \sum_{s\in S}i^s i=1k(1)kiCkisSis

然后我们要做的就是求出所有的 i s i^s is 。因为 i i i 的范围很小,所以仍旧考虑 DP 。设 f i , s , t , l , h f_{i,s,t,l,h} fi,s,t,l,h 表示在 [ s , t ] [s,t] [s,t] 区间内,最左边的 a i = l a_i=l ai=l ,最右边的 a i = h a_i=h ai=h 的所有方案的 ∑ i s \sum i^s is 。然后就很方便转移了,用线段树维护一下就好了。

这种高妙的做法我倒是想不出来,但是暴力的 70 分没有拿到实在是不应该,以后考试不能睡觉了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e4 + 10, mod = 1e9 + 7, K = 6, H = 4;
int n, q, a[N], c[K][K], sum[K][N];

template<class T>inline void read(T &x){
	x = 0; bool fl = 0; char c = getchar();
	while (!isdigit(c)){if (c == '-') fl = 1; c = getchar();}
	while (isdigit(c)){x = (x<<3)+(x<<1)+c-'0'; c = getchar();}
	if (fl) x = -x;
}

void add(int &x, int y){x += y; if (x >= mod) x-= mod;}
int pls(int x, int y){x += y; return (x >= mod ? x-mod : x);}

void init(){
	for (int i = 1; i <= 5; ++ i){
		sum[i][1] = i;
		for (int j = 2; j < N; ++ j)
			sum[i][j] = (1LL*sum[i][j-1]*(i+1)+i)%mod;
	}
	c[0][0] = 1;
	for (int i = 1; i <= 5; ++ i){
		c[i][0] = 1;
		for (int j = 1; j <= i; ++ j)
			c[i][j] = c[i-1][j-1]+c[i-1][j];
	}
}

namespace SegT
{
	#define ls (u<<1)
	#define rs (u<<1^1)
	#define mid (l+r>>1)
	struct node{
		int f[K][H][H];
		node(){}
		node(int c, int len){
			memset(f, 0, sizeof f);
			for (int i = 1; i <= 5; ++ i)
				f[i][c][c] = sum[i][len];
		}
		node operator + (node u){
			node ret;
			for (int i = 1; i <= 5; ++ i)
				for (int j = 1; j <= 3; ++ j)
					for (int k = j; k <= 3; ++ k)
						ret.f[i][j][k] = pls(f[i][j][k], u.f[i][j][k]);
			for (int i = 1; i <= 5; ++ i)
				for (int j = 1; j <= 3; ++ j)
					for (int k = j; k <= 3; ++ k)
						for (int p = j; p <= k; ++ p)
							for (int w = p; w <= k; ++ w)
								add(ret.f[i][j][k], 1LL*f[i][j][p]*u.f[i][w][k]%mod);
			return ret;
		}
	}t[N<<2];
	int laz[N<<2];
	void push_up(int u){
		t[u] = t[ls]+t[rs];
	}
	void push_down(int u, int ln, int rn){
		if (laz[u]){
			t[ls] = node(laz[u], ln);
			laz[ls] = laz[u];
			t[rs] = node(laz[u], rn);
			laz[rs] = laz[u];
			laz[u] = 0;
		}
	}
	void build(int u, int l, int r){
		if (l == r){
			t[u] = node(a[l], 1);
			return;
		}
		build(ls, l, mid);
		build(rs, mid+1, r);
		push_up(u);
	}
	void modify(int u, int l, int r, int L, int R, int C){
		if (L <= l && r <= R){
			t[u] = node(C, r-l+1);
			laz[u] = C;
			return;
		}
		push_down(u, mid-l+1, r-mid);
		if (L <= mid) modify(ls, l, mid, L, R, C);
		if (mid < R) modify(rs, mid+1, r, L, R, C);
		push_up(u);
	}
	node query(int u, int l, int r, int L, int R){
		if (L <= l && r <= R) return t[u];
		push_down(u, mid-l+1, r-mid);
		if (mid < L) return query(rs, mid+1, r, L, R);
		else if (R <= mid) return query(ls, l, mid, L, R);
		else return query(ls, l, mid, L, R)+query(rs, mid+1, r, L, R);
	}
}
using namespace SegT;

int main()
{
	freopen("sendpoints.in", "r", stdin); freopen("sendpoints.out", "w", stdout);
	read(n); read(q);
	for (int i = 1; i <= n; ++ i) read(a[i]);
	init();
	build(1, 1, n);
	for (; q--; ){
		int opt, l, r, x;
		read(opt); read(l); read(r); read(x);
		if (opt == 1) modify(1, 1, n, l, r, x);
		else{
			node nod = query(1, 1, n, l, r);
			int ans = 0;
			for (int i = x, t = 1; i >= 1; -- i, t ^= 1){
				int tmp = 0;
				for (int j = 1; j <= 3; ++ j)
					for (int k = j; k <= 3; ++ k)
						add(tmp, nod.f[i][j][k]);
				if (t) add(ans, 1LL*c[x][i]*tmp%mod);
				else add(ans, mod-1LL*c[x][i]*tmp%mod);
			}
			printf("%d\n", ans);
		}
	}
	return 0;
}

后记

感觉总体难度不大的一套题,但是我却一题都没有 AC 。主要原因还是在态度上。

只剩最后几天了,对每一道题都需要非常认真,把能拿的分全都拿到。

还有,晚上早点睡觉,中午也早点午睡,早上做题不要犯困。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值