2021.10.18模拟赛

今天的出题人游戏似乎玩得很多呢qwq

T1 loopers

我想永远当你最珍贵的宝物………
  首先,我永远恨概率期望!!!!!!其实就是因为我太蒻了,做不来题
  题目说了,下一次走到一个点 i 的概率为 a i / ∑ 没 选 过 的 点 j a j a_i / \sum_{没选过的点j}a_j ai/jaj,所以我们得到在第一个点之前选到第 i 个点的概率为: a i a 1 + a i \frac{a_i}{a_1+a_i} a1+aiai。再乘上这个节点的值 a i a_i ai 就是这个点的数学期望。然后我们把所有点的期望加起来就得到总的期望:
E = ∑ i = 2 n a i 2 a i + a 1 + a 1 E = \sum_{i=2}^{n} \frac{a_i^2}{a_i+a_1} + a_1 E=i=2nai+a1ai2+a1
  我们因为要对答案取模,所以要算出 1 ~ a m a x a_{max} amax 的所有逆元,所以我们用线性递推的方法。最后的时间复杂度是 O ( 1 0 8 + n ) O(10^8 + n) O(108+n)

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define MAXN 10001000
#define MAXA int(1e8 + 100)
#define MOD int(1e9 + 7)

int read(){
	int x = 0; char c = getchar();
	while(c < '0' or c > '9') c = getchar();
	while('0' <= c and c <= '9'){
		x = x * 10 + c - '0'; c = getchar();
	}
	return x;
}

int n = 0;
int a[MAXN] = { 0 };

int inv[MAXA] = { 0 };

int main(){
	n = in;
	for(int i = 1; i <= n; i++){
		a[i] = in;
	}
	inv[1] = 1;
	for(int i = 2; i <= 100000000; i++)
		inv[i] = (1ll * MOD - MOD / i) * inv[MOD % i] % MOD;
	
	long long ans = 0;
	for(int i = 2; i <= n; i++){
		ans = (ans + 1ll * a[i] * a[i] % MOD * inv[a[i] + a[1]] % MOD) % MOD;
	}
	ans += a[1]; ans %= MOD;
	printf("%lld\n", ans);
	return 0;
}

T2 rewrite

纵使新月的微光、明灭的星辰、街头的灯火全然散尽
纵使黑暗吞噬一切,我仍不会离你而去
  本题正解要用到斜率优化 DP,我太蒻了,不会打斜率优化不过最近正在学,浅谈斜率优化,等学会了再回来打正解。所以这里先打一个50分的暴力。
  首先让我们看看 main 函数里的东西(输入部分):

n = in; k = in;
for(int i = 1; i <= n; i++){
	a[i] = in;
	if(a[i] == 0){
		// idx[i] 是第 i 个 0 的位置 cnt 是 0 的数量
		idx[++cnt] = i;
	}
}

  首先第二个点先说这个因为这个最简单,满足所有的的位置都不是 0 ,所以直接 nlogn 暴力树状数组求逆序对就好了。

inline ll lowbit(ll x){
	return x & -x;
}

inline void add(ll index, ll val){
	for(; index <= n; index += lowbit(index)){
		c[index] += val;
	}
}

inline ll query(ll x){
	ll ans = 0;
	for(; x; x -= lowbit(x)){
		ans += c[x];
	}
	return ans;
}

ll inverPair(ll n, ll a[]){
	for(ll i = 1; i <= 4 * k; i++) c[i] = 0;
	ll ans = 0;
	for(ll i = n; i >= 1; i--){
		ans += query(a[i] - 1ll);
		add(a[i], 1ll);
	}
	return ans;
}

// 下面的内容在 main() 函数里
if(cnt == 0){                              // 0 的数量为 0
	printf("%lld\n", inverPair(n, a));
	return 0;	
}

  然后是第一个点打最暴力的暴力也最多 8 8 8^8 88 可以过,那就直接 dfs 走起。

ll dfs(ll x){
	ll ans = 0;
	if(x > cnt - 1ll){
		return inverPair(n, a);
	}
	ll p = -1;
	for(ll i = 1; i <= k; i++){
		a[idx[x + 1ll]] = i;
		ans = max(ans, dfs(x + 1ll));
		a[idx[x + 1ll]] = 0;
	}
	return ans;
}

// 下面的内容在 main() 函数里
if(n <= 8 and k <= 8){
	printf("%lld\n", dfs(0));
}

  第三个点只有一个位置是 0,所以暴力枚举的话就是 klogn,能过,那就枚举!

// 下面的内容在 main() 函数里
if(cnt == 1){                                // 只有一个 0
	ll ans = 0;
	for(ll i = 1ll; i <= k; i++){
		a[idx[1]] = i;
		ans = max(ans, inverPair(n, a));
		a[idx[1]] = 0;
	}
	printf("%lld\n", ans);
	return 0;
}

  第四和第五个点,全是0,这个就有点点麻烦了,但其实也还好。显然我们要填写的数段一定是不上升的就比如说若干个 k 然后若干个 k-1 然后若干个 k-2 … 最后若干个 1。而且我们要让每一段尽量平均。所以我们每段取 ⌊ n k ⌋ \lfloor \frac{n}{k} \rfloor kn 个数,剩下的 n mod k 个数就都填 1就行了。所以我们的答案是:
a n s = ∑ i = 1 k − 1 ⌊ n k ⌋ ( n − i ⌊ n k ⌋ ) ans = \sum_{i=1}^{k-1}\lfloor \frac{n}{k} \rfloor \Big( n - i \lfloor \frac{n}{k} \rfloor \Big) ans=i=1k1kn(nikn)

// 下面的内容在 main() 函数里
if(cnt == n){
	ll ans = 0;
	for(ll i = 1ll; i <= k - 1; i++){
		ans += (n / k) * (n - i * (n / k));
	}
	printf("%lld\n", ans);
	return 0;
}

  愉快的拿到 50 分。


T3 pockets

眼前这个少女的笑容,和她这副喃喃自语的样子,仿佛与我记忆深处的某个东西产生了共鸣。
月光洒落在少女的侧脸上,居然让我感到一种几乎要落泪的怀念感。
那一定是……
  算法标签:差分,线段树,哈希
  很好,不会!不过我还是打了一个线段树,本来想着骗个 10 分就好了,结果骗出来 35 分qwq。
  下面是骗分代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define in read()
#define MAXN 500500

int read(){
	int x = 0; char c = getchar();
	while(c < '0' or c > '9') c = getchar();
	while('0' <= c and c <= '9'){
		x = x * 10 + c - '0'; c = getchar();
	}
	return x;
}

int n = 0; int q = 0; int P = 0;
int a[MAXN] = { 0 };

struct SegTree{
	int l; int r;
	ll dat; int lazy;
}t[4 * MAXN];

int ls(int p){
	return p << 1;
}
int rs(int p){
	return p << 1 | 1;
}

void pushup(int p){
	t[p].dat = t[ls(p)].dat + t[rs(p)].dat;
}

void pushdown(int p){
	t[ls(p)].dat += (ll)t[p].lazy * (ll)(t[ls(p)].r - t[ls(p)].l + 1);                  
	t[rs(p)].dat += (ll)t[p].lazy * (ll)(t[rs(p)].r - t[rs(p)].l + 1);
	t[ls(p)].lazy += t[p].lazy; t[rs(p)].lazy += t[p].lazy;
	t[p].lazy = 0;
}

void build(int p, int l, int r){
	t[p].l = l; t[p].r = r;
	if(l == r){
		t[p].dat = a[l] % P; return;
	}
	int mid = (l + r) >> 1;
	build(ls(p), l, mid);
	build(rs(p), mid + 1, r);
	pushup(p);
}

void update(int p, int l, int r, int val){
	if(l <= t[p].l and t[p].r <= r){
		t[p].dat += (ll)(t[p].r - t[p].l + 1) * (ll)val;
		t[p].lazy += val; return;
	}
	pushdown(p);
	int mid = (t[p].l + t[p].r) >> 1;
	if(l <= mid) update(ls(p), l, r, val);
	if(r > mid)  update(rs(p), l, r, val);
	pushup(p);
}

ll query(int p, int l, int r){
	if(l <= t[p].l and t[p].r <= r)
		return t[p].dat;
	pushdown(p);
	ll ans = 0;
	int mid = (t[p].l + t[p].r) >> 1;
	if(l <= mid) ans += query(ls(p), l, r);
	if(r > mid)  ans += query(rs(p), l, r);
	return ans;
}

int main(){
	n = in; q = in; P = in;
	for(int i = 1; i <= n; i++){
		a[i] = in;
	}
	build(1, 1, n);
	for(int i = 1; i <= q; i++){
		int op = in; int l = in;  int r = in; int k = in;
		if(op == 1){
			update(1, l, r, k);
		}
		else if(op == 2){
			bool is = true;
			while(k--){
				if((query(1, l, l) % P) != (query(1, r, r) % P)){
					is = false; break;
				}
				l++; r++;
			}
			if(is) printf("ye5\n");
			else   printf("n0\n");
		}
	}
	return 0;
}

T4 dohnadohna

一起来干坏事吧
开玩笑,谁会 T4 啊 打了一个 30 分做法,如果没有删除节点的话,就直接在有向无环图 (DAG) 上跑背包就好了。然后暴力枚举每个点并删除,复杂度 O(nmk)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值