首页 > 其他分享 >3.30 月赛

3.30 月赛

时间:2024-04-19 22:26:06浏览次数:15  
标签:月赛 val int LL range 3.30 dp MOD

链接:https://ac.nowcoder.com/acm/contest/34980/

A. 思维?

比较简单,可以发现在三位以外,五位以内就可以凑出来17,那么当 n >= 4 是一定 yes 的,超出的位数只可能是 偶数 或者 奇数,只要使得计算 17 所用的位数分别为 偶数个 和 奇数个 就可以了,剩下的 两两相减得到 1, 再相乘就行。

 

B. 高精度, dp

用python会很方便,不过需要提前处理好 i-j 所表示的数

f[i][j]表示前 i 位 用了 j 个加号所得到的结果

转移方程为 f[i][j] = min(f[i][j], f[k][j-1] + val[k+1][j])

还有一个需要优化的是,如果直接枚举 中间点k,时间复杂度是 n^3 的,这样一定会超时

但考虑一下,如果要想结果最小,那肯定是要把整个段平均分开的,所以这个切割的长度最佳时应该是 n / m + 1,python内应为 n // m + 1,表示先地板除,再加一,这样就是 n^2 的了

import sys
from math import inf

n, m = map(int, sys.stdin.readline().split())
s = " " + sys.stdin.readline().strip()

val = [[0] * (n + 1) for _ in range(n + 1)]

for i in range(n + 1):
    if i != 0:
        val[i][i] = int(s[i])
    for j in range(i + 1, n + 1):
        val[i][j] = val[i][j - 1] * 10 + int(s[j])

mi = [[] for _ in range(m + 1)]
for i in range(m + 1):
    mi[i].append(0)

len = n // m + 1
dp = [[inf] * (m + 1) for _ in range(n + 1)]
for i in range(n + 1):
    dp[i][0] = val[0][i]
dp[0] = [0] * (m + 1)

for i in range(1, n + 1):
    for j in range(1, m + 1):
        for k in range(max(0, i - len), i):
            dp[i][j] = min(dp[i][j], dp[k][j - 1] + val[k + 1][i])

print(dp[n][m])

 

E. 前缀积,逆元

记 Ai 为前 i 项之积,要想找到符合这样要求的序列 [ i, j ]

则一定满足 Ai * Aj_1 mod MOD = x

那么只需要找到有多少个 Aj的逆 mod MOD = x * Ai_1 就好了

只要从左到右遍历一遍区间,统计一下 Ai , Ai+1, ... , Aj 对应的逆元,然后从右到左再重新遍历一遍,累加答案即可 

但还有一个问题就是如果 a[i] = 0,这样会导致一些错误,那其实这个时候只需要分区间来执行上述操作就好了

还有问题就是如果 x == 0,那这时候只需要用 所有的子区间个数 - 区间内没有一个元素为 0 的区间数即可

折叠代码
 #include<bits/stdc++.h>

const int MAXN = 5e5 + 10;
const int MOD = 998244353;
using namespace std;
typedef long long LL;

LL n, x;
LL a[MAXN];
map<LL, int> mp;
LL pro = 1;
LL ans;
int poi[MAXN], cnt;

LL exgcd(int a,int b,LL &x,LL &y)
{
	if(!b)
	{
		x = 1;
		y = 0;
		return a;	
	}	
	LL d = exgcd(b,a%b,y,x);
	y -= (a / b) * x;
	return d;
}

LL inv(int a,int b)
{
	LL x,y;
	if(exgcd(a,b,x,y) != 1) return -1;
	return (x % MOD + MOD) % MOD;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin >> n >> x;
	for(int i = 1;i <= n;i++) 
	{
		cin >> a[i];
		a[i] %= MOD;
		if(!a[i]) poi[++cnt] = i;

	}
	poi[cnt+1] = n + 1;	
	if(!x){
		for(int i = 0;i <= cnt;i++){
			LL len = 1LL * poi[i+1] - 1 - (poi[i]+1) + 1;
//			cout << len << '\n';
			ans += len * (len + 1) / 2;
		}		
		cout << 1LL * n * (n + 1) / 2 - ans << '\n';
		return 0;
	}
	
//	cout << inv(2, MOD) << '\n';
//	cout << inv(6, MOD)

	for(int i = 0;i <= cnt;i++){
		mp.clear();
		mp[1]++;
		pro = 1;
		for(int j = poi[i] + 1; j < poi[i+1];j++){
			pro = pro * a[j] % MOD;
			mp[inv(pro, MOD) % MOD]++;
		}
		for(int j = poi[i+1]-1;j > poi[i];j--){
			mp[inv(pro, MOD) % MOD]--;
			ans += mp[x*inv(pro, MOD)%MOD];
			pro = pro * inv(a[j], MOD) % MOD;			
		}
	}
	cout << ans << '\n';
	return 0;
}

 

I.

硬币问题,比较简单,只要分别处理出来对于给定的硬币,二人分别所需的最少数目就可以

 

J. 二分,前缀和,最长不上升子序列 nlogn 做法

比较关键的地方在于 check 函数,要如何确认对于一个值,能否将区间划分为 k 段,使得每一段的平均值大于等于该值

贪心肯定是不行的,局部最优解不一定是全局最优解,对于一个特别大的数,无论是将它分配到一个尽可能小的区间还是尽可能大的区间都可能是错误的。

这时候考虑前缀和

定义前缀和为 summ[i] = summ[i-1] + a[i] - ave

那么对于一个合法的 ave, 应该有 summ[0] = 0, summ[n] >= 0

那么要将区间划分为 k 段,那么对于每一个区间的右边界 R, 应当有 summ[0] <= summ[R1] <= summ[R2] <= ... <= summ[n]

那么这样只需要求解一下长度为 n 的最长上升子序列就好了,需要注意的是一定要以 summ[0] = 0 开头。

可以先把 summ 中所有大于等于 0 的项筛出来, 然后再求解

这里只贴贴一个 nlogn的求解最长不降子序列的代码

还有一道比较类似的题,也是让区间的均值最大。

https://www.acwing.com/problem/content/104/

  

查看代码

#include<bits/stdc++.h>

const int MAXN = 1e5 + 10;
using namespace std;

int n;
int a[MAXN], b[MAXN];
int poi[MAXN];
int len;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin >> n;
	for(int i = 1;i <= n;i++) cin >> a[i];
	vector<int> f(n+1, 1e5+1);
	f[0] = 0;
	for(int i = 1;i <= n;i++) {
		if(a[i] > f[len]) f[++len] = a[i];
		else {
			auto p = lower_bound(f.begin(), f.end(), poi[b[i]]) - f.begin();
			f[p] = a[i];			
		}

	} 
	cout << len << '\n';
	return 0;
}

 

标签:月赛,val,int,LL,range,3.30,dp,MOD
From: https://www.cnblogs.com/xxx3/p/18118142

相关文章

  • 牛客小白月赛91 (小白被虐记)
    A.Bingbong的化学世界(签到题)思路: 你会发现当a[1][4]=='.'是是o-甲乙苯 a[6][4]=='|'是是p-甲乙苯另外则是m-甲乙苯 Code:#include<bits/stdc++.h>usingnamespacestd;constintN=10;chara[N][N];intmain(){ios::sync_with_std......
  • 2024.3.30C笔记
    2024.3.30笔记1.转义字符intmain(){printf("abcd\b");//回退一个字符,隐藏\b算一个字符return0;}2.函数调用语句函数调⽤的时候,也会加上分号,就是函数调⽤语句#include<stdio.h>intAdd(intx,inty){returnx+y;}intmain(){printf("hehe\n")......
  • 上海计算机学会2020年5月月赛C++丙组T2计算GPA
    题目背景GPA是GradePointAverage的简写,是高校采用的一种评估学生成绩的制度。题目描述要计算一个学生的GPA,先将每门学科的等第换算成为一个绩点,规则为:等第 A 为 44 分;等第 B 为 33 分;等第 C 为 22 分;等第 D 为 11 分;如果有 + 号后缀,则加 0.30.3 ......
  • 牛客小白月赛90----->D.小A的线段(easy version)
    1,思路:因为只有10个线段所以直接暴力枚举所有方案,看满足条件的方案有多少个,我这里用的是二进制枚举(dfs也可以),时间复杂度是:1024*1e5=1e8,这个时间复杂度是可以接受的。2.代码:#include<iostream>#include<algorithm>#include<cmath>#include<cstring>usingnamespacestd;......
  • 牛客小白月赛90题解
    A.小A的文化节#include<bits/stdc++.h>#defineintlonglong#defineendl"\n"usingnamespacestd;constintN=1e5+10,mod=1e9+7;intn,m,a[N];voidsolve(){ cin>>n>>m;for(inti=1;i<=n;i++)cin>>a[i];intres=0......
  • 牛客小白月赛90 A~D
    A-小A的文化节(Nowcoder78306A)题目大意将n个数中选定的数相加并输出。解题思路数据量很小只需int即可。神奇的代码#include<bits/stdc++.h>usingnamespacestd;usingLL=longlong;intmain(){ios::sync_with_stdio(false);cin.tie(nullpt......
  • 2024.3.30 模拟赛
    A数列删除至少删除\(m\)个数,意思就是最多保留\(n-m\)个数。删除的总和最小,意思就是保留的总和最大。非降子序列问题可以用经典的动态规划来解决。用\(f[i][j]\)表示,当前选的最后一个数是\(a[i]\),一共选了\(j\)个数,选的数总和最大是多少。转移就是枚举上一个数\(a[......
  • 3.30 模拟赛 T3 记录
    题面首先先可以发现对于限制\(\min_{i\in[l,r]}a_i\leqr-l+1\),的任意一个右端点,能贡献的\(l\)肯定是一个可以确定的前缀,这一部分可以用单调队列提前预处理出每个前缀记为\(pre_i\)。同理对于任意一个左端点也对应可以转移到一个确定的后缀,也预处理出来记为\(nxt_i\)。......
  • 3.30毕设
    web系统通过axios传递参数首先安装axios,通过过下面这条命令npminstallaxios-g其中-g:表示全局安装,将会安装在你配置的项目目录下。执行GET请求  执行POST请求 ......
  • 3.30
    所花时间:3小时代码量:309博客篇:1使用自定义表格查询示例,总结统计:packagecom.example.studyapplication;importandroid.graphics.Color;importandroid.os.Bundle;importandroid.util.Log;importandroid.view.LayoutInflater;importandroid.view.View;importandr......