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

仍在施工...

165pts,Rank18

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

题目列表:

A.abc猜想

B.简单的排列最优化题

C.简单的线性做法题

D.简单的线段树题

A.abc猜想

题意:

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

赛时分析:

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

正解:

ab 显然快速幂,模什么呢,模 c 的话之后再 ÷c 结果显然为 0,所以我们模 c2

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=1nπii,定义它的 k-移位为新的排列 πk=(πnk+1,πnk+2,,πn,π1,,πnk)。特别的,π0=π

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

赛时分析:

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

正解:

枚举移位的位数 k,预处理初始的总和 sum,以及小于 0 的个数 sm 和大于等于 0 的个数 bi,对于每一个 ksum 都可以由 lastsum+smbi 加上特判最后一位数转移,考虑转移 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 @   Aqr_Rn  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 为DeepSeek添加本地知识库
· 精选4款基于.NET开源、功能强大的通讯调试工具
· DeepSeek智能编程
· 大模型工具KTransformer的安装
· [计算机/硬件/GPU] 显卡
点击右上角即可分享
微信分享提示