「模拟赛」暑期集训CSP提高模拟3(7.20)

仍在施工...

$165 pts, Rank 18$

B 题挂了 45 分,不然可以 AC 两道题的,呜

题目列表:

A.abc猜想

B.简单的排列最优化题

C.简单的线性做法题

D.简单的线段树题

A.abc猜想

题意:

给定三个正整数 \(a,b,c\),你需要求出 \(a^b\) 除以 \(c\) 并向下取整得到的值对 \(c\) 取模的结果。

赛时分析:

大概十天之前 和 \(DrRatio\)\(GGrun\) 玩洛谷随机跳题做过一道类似且难度大于等于这个的题,开赛没十秒, GGrun:做过做过这个,于是十分钟切了。

正解:

\(a^b\) 显然快速幂,模什么呢,模 \(c\) 的话之后再 \(\div c\) 结果显然为 0,所以我们模 \(c^2\)

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

ll a, b, c;

ll qpow(ll x, ll y, ll mod){
	ll ans = 1;
	while(y)
	{
		if(y & 1) ans = ans * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return ans;
}

signed main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);

	scanf("%lld%lld%lld", &a, &b, &c);

	ll ans = qpow(a, b, c * c) / c;
	ans = ans % c;

	printf("%lld", ans);

	return 0;
}

B.简单的排列最优化题

题意:

对长度为 \(n\) 的排列 \(π=(π1,π2,…,πn)\),定义它的权值 \(wt(π)=∑_{i=1}^n∣π_i−i∣\),定义它的 k-移位为新的排列 \(πk=(πn−k+1,πn−k+2,…,πn,π1,…,πn−k)\)。特别的,\(π_0=π\)

现在给定长度为 \(n\) 的排列 \(π\),请找到一个 \(k∈[0,n−1]\),使得 \(wt(πk)\) 最小。

赛时分析:

上来想半天没想到什么正解,于是决定先打上暴力 \(O(n^2)\), 还写了个循环,发现每次状态完全可以由上次转移而来,不需要每次都 \(O(n)\) 计算,于是想如何转移,细节很多,打了一个多小时过了所有样例,但数组开小了加之有个地方的 \(=\) 忘了写,挂了\(45pts\)

正解:

枚举移位的位数 \(k\),预处理初始的总和 \(sum\),以及小于 0 的个数 \(sm\) 和大于等于 0 的个数 \(bi\),对于每一个 \(k\)\(sum\) 都可以由 \(lastsum+sm-bi\) 加上特判最后一位数转移,考虑转移 \(sm,bi\) 即可。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 4e6 + 10;

int n, a[N];
int sm_0, bi_0, b_s[N];
ll sum;

signed main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);

	scanf("%d", &n);
	for(int i=1; i<=n; i++){
		scanf("%d", &a[i]);
		sum += (long long)abs(a[i] - i);
		if(a[i] - i < 0) sm_0++;
		else{
			bi_0++;
			if(a[i] != n) b_s[a[i]-i]++;
		}
	}

	int ansid = 0; ll ans = sum;
	for(int k=1; k<n; k++){
		
		if(a[n-k+1] >= n) bi_0--;
		if(a[n-k+1] < n) sm_0--;
		bi_0 -= b_s[k-1]; sm_0 += b_s[k-1];

		sum = sum + sm_0 - bi_0 + abs(a[n-k+1] - 1) - abs(a[n-k+1] - n);

		if(a[n-k+1] != n) b_s[a[n-k+1]-1+k]++;

		if(a[n-k+1] < 1) sm_0++;
		if(a[n-k+1] >= 1) bi_0++;

		if(sum < ans){
			ans = sum, ansid = k;
		}
	}

	printf("%d %lld", ansid, ans);


	return 0;
}

C.简单的线性做法题

D.简单的线段树题

正解:

雀氏线段树,我们发现对于每一棵树,最多开平方到 1 以后便一直为 1,我们在只需维护一个区间总和,当区间总和等于区间大小时,那么该区间内所有数都为 1,不再需要递归更新,直接返回即可。

code:

#include<bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;

const int N = 4e6 + 10, N1 = 1e6 + 10;

int n, m;
ll a[N1];

namespace Segment_tree
{
	#define lson rt<<1
	#define rson rt<<1|1
	ll sum[N];

	void pushup(int rt){
		sum[rt] = sum[lson] + sum[rson];
	}

	void build(int rt, int l, int r){
		if(l == r){
			sum[rt] = a[l];
			return;
		}

		int mid = l + r >> 1;
		build(lson, l, mid);
		build(rson, mid+1, r);

		pushup(rt);
	}

	void update(int rt, int l, int r, int ql, int qr){
		if(l == r){
			sum[rt] = sqrt(sum[rt]);
			return;
		}
		if(sum[rt] == r - l + 1) return;

		int mid = l + r >> 1;
		if(ql <= mid) update(lson, l, mid, ql, qr);
		if(qr > mid) update(rson, mid+1, r, ql, qr);

		pushup(rt);
	}

	ll find(int rt, int l, int r, int ql, int qr){
		if(ql <= l and r <= qr){
			return sum[rt];
		}

		int mid = l + r >> 1; ll ans = 0;
		if(ql <= mid) ans += find(lson, l, mid, ql, qr);
		if(qr > mid) ans += find(rson, mid+1, r, ql, qr);
		return ans;
	}	
}

signed main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);

	scanf("%lld", &n);
	for(int i=1; i<=n; i++){
		scanf("%lld", &a[i]);
	}

	Segment_tree::build(1, 1, n);

	scanf("%lld", &m);
	for(int i=1; i<=m; i++){
		int op, l, r;
		scanf("%lld%lld%lld", &op, &l, &r);
		if(op == 1){
			printf("%lld\n", Segment_tree::find(1, 1, n, l, r));
		}
		else{
			Segment_tree::update(1, 1, n, l, r);
		}
	}


	return 0;
}
posted @ 2024-07-21 16:30  Aqr_Rn  阅读(17)  评论(0编辑  收藏  举报