131018总结

本文详细解析了解题过程,并提供了多种算法的应用实例,涵盖了从基础到进阶的多个领域,包括数学运算、数论、搜索算法、动态规划、树形结构处理、字符串操作等。通过具体的例子和代码实现,深入探讨了如何解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

131017试题 – 解题报告

A

a.cpp/c/pas  a.in a.out

1s64MB

求n个数的最小公倍数

 

输入

第一行一个数n

第二行有n个数,每两个数直接用空格隔开

 

样例

输入

3

4 5 6

输出

60

 

数据范围

对于30% n<=2 ai<=10000

对于60% 答案小于10^18

对于100% n<=100 ai<=10^9

 

 

 

 

首先,思想很简单,分解质因数即可,如

         26= 21 + 30 + 50 + 70 + 110+131

         36= 22 + 32 + 50 + 70 + 110+ 130

那么显然lcm就为21 + 32 + 131

 

那么打一个素数表就可以解决了

 

值得注意的是,唯一分解定律中,如果1~sqrt(n)中分解完了,最后剩下的数不为1的话,那么他也是一个质数,所以千万别把这一个质数忘了


还有,记得写高精乘法,不过我的高精好像写跪了,正在调试中……


(代码就算了)



 



B

b.cpp/c/pas  b.in b.out
1s 64MB


题目描述
给定一个长度为n的数列,每个数可以加上或者减去小于等于X的数,使数列变成严格递增的正整数数列,最小的X


输入格式
第一行包含一个数n,表示数列中数的个数
第二行包含n个用空格隔开的数ai


输出格式
一行,包括一个数表示最小的X


输入样例


3
1 2 2


输出样例


1


数据范围
30%的数据满足n=5,0<=ai<=10
60%的数据满足n=1000,0<=ai<=1000
100%的数据满足n=10^5,0<=ai<=10^9




很简单的二分,二分出题目要求的最小x,然后验证即可


验证的时候可以贪心,可以想想,如果当前这个数比上一个数大,那么肯定把他减小(并且在合法范围内减得越小越好!这样才能给后面留出更多的改变空间,换句话说,这样才能保证最优)

如果当前比上一个数小,那么不用说可定要加上去,如果+x还比上一个小(或者相等),那么果断返回false(也就是当前二分出来的值肯定不满足题意),如果比上一个数大了,我们就把它加到 (上一个数+1) 值  (同理,这样也是为了让后面变得更优)


还有就是XJ坑,明明是正整数序列,非要告诉我们最后可以有0(题目意思是最后加减出来的序列必须是正整数),好多人都是这里错了,找了好久的错……


可以看看代码

#include <cstdio>//By Jiangzh
const int N = 100000 + 10;
typedef long long LL;

int n;
LL a[N];

bool check(LL mid)
{
	LL last = 0; 
	for(int i = 1; i <= n; i++)
	{
		if(a[i] > last)
		{
			int t = a[i] - last - 1;
			// a[i] - t = last + 1
			if(t > mid) t = mid;
			last = a[i] - t;
		}
		else {
			if(a[i] + mid <= last) return 0;
			int t = last - a[i] + 1;
			last = a[i] + t;
		}
	}
	return 1;
}

void solve()
{
	LL L = 0, R = 0x3f3f3f3f3f3f3f3fll;
	while(L < R)
	{
		LL mid = L + (R - L) / 2;
		if(check(mid)) R = mid;
		else L = mid + 1;
	}
	printf("%I64d\n", R);
}

int main()
{
	freopen("b.in", "r", stdin);
	freopen("b.out", "w", stdout);
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	solve();
	return 0;
}







C

c.cpp/c/pas  c.in c.out
1s 64MB


一个01串 想要将该串切成几段  使得每段对应的十进制值都是5^i的形式  即5^0,5^1,5^2....
问最小需要切成几段 注意每段不能有前导0 比如01是不合法


输入
一行,即一个01串


输出
一行,即为答案


样例


输入
1101
输出
2




数据范围
30%  n<=10
60%  n<=20
100% n<=50





标解是dp,但是dfs其实也可以轻松过

dfs的过程不用说了吧,找到合法的位置,然后枚举是否在这里截断(千万不能贪心找最后一个能截断的位置,因为会影响其他的分配)

看似2^n的复杂度,但是题目很特殊,找最小值,直接加一个最优化剪枝,轻松过


C++ AC Code

#include <cstdio>// By Jiangzh
#include <cstring>
#include <algorithm>
using std::min;

char s[100];
int len;
int Five = 0x3f3f3f3f;

bool check(long long sum)
{
	if(sum % 5 != 0) return 0;
	while(sum > 1 && sum % 5 == 0)
	{
		sum /= 5;
	}
	return sum == 1;
}

void dfs(int le, int times)
{
	if(times >= Five) return;
	//printf("%d %d\n", le, times);
	if(le == 0) {Five = min(Five, times); return;}
	int k = 1; long long sum = 0;
	for(int i = le - 1; i >= 0; i--, k *= 2)
	{
		int c = s[i] - '0';
		sum += (long long)c * k;
        if((sum == 1 || check(sum)) && c != 0) dfs(i, times+1);
	}
}

int main()
{
	freopen("c.in", "r", stdin);
	freopen("c.out", "w", stdout);
	scanf("%s", s); len = strlen(s);
	dfs(len, 0);
	if(Five == 0x3f3f3f3f) printf("-1\n");
	else printf("%d\n", Five);
	return 0;
}










131018试题 - 解题报告


Xor

xor.c/cpp/pas

1s 64M

描述

求1到n的异或值,即1^2^3.....^n

 

输入(xor.in)

一个数n

输出(xor.out)

一个数,表示所求的值

 

样例

 

输入

3

输出

0

 

数据范围

50%       1<=n<=10^6

100%      1<=n<=10^18



看看范围,10^18,出了O(1)的算法还能怎么做?

就算不知道也该写50分的朴素吧,全部打出来也应该能发现规律的

具体规律自己找找吧


C++ AC Code

#include <cstdio>// By Jiangzh

long long n;

int main()
{
	freopen("xor.in", "r", stdin);
	freopen("xor.out", "w", stdout);
	scanf("%I64d", &n);
	long long num = n / 4 * 4;
	n %= 4;
	long long ans = num;
	for(int i = 1; i <= n; i++)
		ans ^= (long long)(i + num);
	printf("%I64d\n", ans);
	return 0;
}




Rooks

rooks.c/cpp/pas

2S/64M

描述

N*N的国际象棋棋盘上有K 个车,第i 个车位于第Ri 行,第Ci 列。求至少被一个车攻击的格子数量

 

输入(rooks.in)

第1 行,2 个整数N,K。

接下来K 行,每行2 个整数Ri,Ci。

 

输出(rooks.out)

1 个整数,表示被攻击的格子数量。

 

样例

 

输入

3 2

1 2

2 2

输出

7

 

数据范围

对于30% 的数据,1<=N<=1000;                 1<=K<=1000

对于60% 的数据,1<=N<=1000000;              1<=K<=1000000

对于100% 的数据,1<=N<=1000000000;        1<=K<=1000000;   1<=Ri,Ci<=N



先吐槽一下cena,用 set 凭什么超时啊!!!

言归正传,看看怎么做(下面是样例)


再看看一个数据



观察一下没有攻击到的格子,刚好是可以重新组成一个矩形的!(很容易证明)

那么我们只需要知道有多少行没有车,有多少列没有车,乘起来就是不会被攻击到的格子,在用总数减去即可


给出两份代码,一个是用set会T掉的,一个是快排

C++ Code // STL set

#include <cstdio>// By Jiangzh
#include <set>
using std::set;

const int N = 1000000 + 10;

int n, m;
set<int> R, C;

int main()
{
	freopen("rooks.in", "r", stdin);
	freopen("rooks.out", "w", stdout);
	scanf("%d%d", &n, &m);
	int hang, lie;
	hang = lie = n;
	for(int i = 1; i <= m; i++)
	{
		int a, b; scanf("%d%d", &a, &b);
		if(!R.count(a))
		{
			R.insert(a);
			hang--;
		}
		if(!C.count(b))
		{
			C.insert(b);
			lie--;
		}
	}
	printf("%I64d\n", (long long)n*n - (long long)hang*lie);
	return 0;
}

C++ AC Code // sort

#include <cstdio>// By Jiangzh
#include <algorithm>
using std::sort;

const int N = 1000000 + 10;

int n, m;
int a[N], b[N];

void work(int *c, int &num)
{
	int last = 0;
	for(int i = 1; i <= m; i++)
	{
		if(c[i]!=last) num--;
		last = c[i];
	}
}

int main()
{
	freopen("rooks.in", "r", stdin);
	freopen("rooks.out", "w", stdout);
	scanf("%d%d", &n, &m);
	int hang, lie;
	hang = lie = n;
	for(int i = 1; i <= m; i++)
	{
		scanf("%d%d", &a[i], &b[i]);
	}
	sort(a+1, a+1+m);
	sort(b+1, b+1+m);
	work(a, hang);
	work(b, lie);
	printf("%I64d\n", (long long)n*n - (long long)hang*lie);
	return 0;
}





Howmany colorings

color.c/cpp/pas

1S/256M

描述

有一颗N 个节点的树,节点用1, 2.....N 编号。你要给它染色,使得相邻节点的颜色不同。有M 种颜色,

用1,2.....M 编号。每个节点可以染M 种颜色中的若干种,求不同染色方案的数量除以(10^9 + 7)的余数。

 

输入(color.in)

第1 行,2 个整数N,M

接下来N 行,第i 行表示节点i 可以染的颜色。第1个整数ki,表示可以染的颜色数量。接下来ki个

整数,表示可以染的颜色编号。

最后N-1 行,每行2个整数Ai,Bi表示边(Ai,Bi).

 

输出(color.out)

一行,即答案

 

样例

 

输入

2 2

1 1

2 1 2

1 2

输出

1

 

数据范围

对于30% 的数据, 1<=N<=10;   1<=M<=4;

对于60% 的数据, 1<=N<=200; 1<=M<=200;

对于100% 的数据,1<=N<=5000;1<=M<=5000  1<=ki  所有ki的和<=1000000




一道比较常规的树形dp,其实也不是树形dp啦,就是很普通的dp,记忆化搜索即可

f[x][c]表示以节点x为根,并且把x涂成颜色c,的方案数


至于怎么转移,也不难,乘法原理,加法原理。每个点选颜色的时候加,各个点之间乘


不过内存似乎有点吃紧,正在修改中……




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值