寒假 1 期总结——知识点

我竟然没有在规定的时间里写完我的帖子(T ^ T) (2.11才写完的蒟蒻(>﹏<))

万恶的寒假集训终于结束啦!!!

不想面对第 2 2 2
在这短短几天里,我们学了亿些知识点。集训结束了,我们当然要简简单单的总结一下这些知识点啦~ <( ̄▽ ̄)/

雷老师这边

函数

函数的定义

在程序设计中,我们会发现一些程序段在程序的不同地方反复出现,此时可以将这些程序段作为相对独立的整体,用一个标识符给它起一个名字,凡是程序中出现该程序段的地方,只要简单地写上其标识符即可。这样的程序段,我们称之为子程序,也就是函数。 ——摘选自《一本通》,有删改

当然,函数也可以使我们的代码看上去更简洁明了,such as this

函数的声明

声明函数十分简单:

函数类型 函数名(参数){ //这里的参数可有可无
	函数体
}

参数被分为了形参实参,区别如下:

int s(int a,int& b){ //a 是形参,b 是实参
	函数体
}

形参和实参的区别:总的来说,形参在函数体中,其传入的变量的值不会改变;实参在函数体中,其传入的变量的值会在函数体中改变
(蒟蒻语文不好,可能表达有误,请见谅,想得到更准确的知识,可以多去 csdn 搜集资料)

函数的分类

对于函数不同的用处,我们将函数大致分为如下几种类型:

英文返回值
int整型
double双精度浮点型
float单精度浮点型
char字符类型
long long int长整型
void无返回型
不难看出,函数的返回值类型与变量的类型相差无几
函数其实不是很难,大家可以尝试一下这道这道以及这道

结构体

结构体的定义

在实际问题中,一组数据往往具有不同的数据类型。为了解决问题,C++语言给出了另一种构造数据类型——结构体。——摘选自《一本通》,有删改

结构体的声明

声明结构体也非常简单:

struct  结构体名称{
	成员变量;
	成员函数;  //结构体中还可以定义函数,但是一般不那么用
};            //分号绝对不能丢!

结构体名称 结构体变量;//当然,也可以定义 数组

结构体的使用

现假设我们定义了一个如下所示的结构体数组:

struct node{  //我们一般喜欢将 node(点) 置为结构体的数组名 
	int a;
	char b;
}x[105];      //在定义结构体数组活结构体变量时,我们也可以将它定义在这个地方

如果我们想要调用 n o d e node node 里面的 a a a 变量,不能只写一个 a ,必须写 x[下标].a

这些是有关结构体的基本操作,大家可以尝试一下 this one and this one

结构体排序

关于这一点,蒟蒻在这篇水帖有一定的介绍,所以直接上例题多么善良

辗转相除法

定义

辗转相除法,一种取两数的最大公因数的算法。因为是欧几里得提出的该算法,所以又称为欧几里得算法(潦草的命名方式
计算公式: g c d ( a , b ) = g c d ( b , a   m o d   b ) gcd(a,b) = gcd(b,a\ mod\ b) gcd(a,b)=gcd(b,a mod b)

证明(不是辣么重要,也不要问我为什么写这玩意。那天晚上喝多了

假设 c = g c d ( a , b ) , 则存在 m , n ,使 a = m c , b = n c ; 假设c = gcd(a,b),则存在m,n,使a = mc, b = nc; 假设c=gcd(a,b),则存在m,n,使a=mc,b=nc;
令 r = a   m o d   b ,即存在 k ,使 r = a − k b = m c − k n c = ( m − k n ) c ; 令r = a\ mod\ b,即存在k,使r = a-kb = mc - knc = (m-kn)c; r=a mod b,即存在k,使r=akb=mcknc=(mkn)c;
故 g c d ( b , a   m o d   b ) = g c d ( b , r ) = g c d ( n c , ( m − k n ) c ) = g c d ( n , m − k n ) c ; 故gcd(b,a\ mod\ b) = gcd(b,r) = gcd(nc,(m-kn)c) = gcd(n,m-kn)c; gcd(b,a mod b)=gcd(b,r)=gcd(nc,(mkn)c)=gcd(n,mkn)c;
则 c 为 b 与 a   m o d   b 的公约数 ; 则c为b与a\ mod\ b的公约数; cba mod b的公约数;
假设 d = g c d ( n , m − k n ) , 则存在 x , y , 使 n = x d , m − k n = y d 假设d = gcd(n,m-kn), 则存在x,y, 使n = xd, m-kn = yd 假设d=gcd(n,mkn),则存在x,y,使n=xd,mkn=yd
故 m = y d + k n = y d + k x d = ( y + k x ) d ; 故m = yd+kn = yd+kxd = (y+kx)d; m=yd+kn=yd+kxd=(y+kx)d;
故有 a = m c = ( y + k x ) d c , b = n c = x d c 故有a = mc = (y+kx)dc, b = nc = xdc 故有a=mc=(y+kx)dc,b=nc=xdc
可得 g c d ( a , b ) = g c d ( ( y + k x ) d c , x d c ) = d c ; 可得 gcd(a,b) = gcd((y+kx)dc,xdc) = dc; 可得gcd(a,b)=gcd((y+kx)dc,xdc)=dc;
由于 g c d ( a , b ) = c , 故 d = 1 ; 由于gcd(a,b) = c, 故d = 1; 由于gcd(a,b)=c,d=1;
即 g c d ( n , m − k n ) = 1 , 故可得 g c d ( b , a   m o d   b ) = c ; 即gcd(n,m-kn) = 1, 故可得gcd(b,a\ mod\ b) = c; gcd(n,mkn)=1,故可得gcd(b,a mod b)=c;
故得证 g c d ( a , b ) = g c d ( b , a   m o d   b ) . 故得证gcd(a,b) = gcd(b,a\ mod\ b). 故得证gcd(a,b)=gcd(b,a mod b).

运用

这玩意作用极为单一,就是求最大公约数。NOIP也考过这玩意。

例题讲解

这道题,我们要让 i , j i,j i,j 同时满足如下两种情况时,答案累加。(亲测,必须全部满足,不然过不了样例)。

  1. i ∗ j = n ∗ m i*j=n*m ij=nm
  2. g c d ( i , j ) = m i n ( n , m ) gcd(i,j)=min(n,m) gcd(i,j)=min(n,m)

非常简单,不用做任何其余的解释,也不需要亮代码。(又水了一道洛谷橙题

快速幂

求幂有很多方法。比如 p o w pow pow 函数。可惜它太慢了(比彬彬还逊)。
所以,我们就需要使用快速幂
想掌握它,必须多写,多背。 蒟蒻给出快速幂程序:

long long int quick_pow(long long int a,long long int b,int c){//a 代表底数,b 代表指数,c 代表所要取模的那个数
	long long int ans=1;                    //定义答案
	while(b){                               //当指数为 0 时跳出循环
		if(b&1){                               //指数为奇数
			ans*=a;                            //答案累乘
			ans%=c;                            //不要忘了取模
		}
		a*=a;                                  //a 也要累乘
		a%=c;                                  //不要忘了取模
		b>>=1;                                 //b 除以 2
	}
	return ans%c;                           //这里的 %c 可要可不要
}

埃氏筛法

算法思想

要得到自然数 n n n 以内的全部素数,必须把不大于 n \sqrt{n} n 的所有素数的倍数剔除,剩下的就是素数。 给出要筛数值的范围 n n n ,找出以内的素数。先用 2 2 2 去筛,即把 2 2 2 留下,把 2 2 2 的倍数剔除掉;再用下一个质数,也就是 3 3 3 筛,把 3 3 3 留下,把 3 3 3 的倍数剔除掉;接下去用下一个质数 5 5 5 筛,把 5 5 5 留下,把 5 5 5 的倍数剔除掉;不断重复下去…。——摘选自《百度百科》

一般的测试中,用埃氏筛已经足够了(其时间复杂度为: O ( n ∗ log ⁡ log ⁡ n ) O(n* \log\log n) O(nloglogn)).很少有丧心病狂的出题人将埃氏筛卡掉。

代码实现(所选例题)

#include<cmath>
#include<cstdio>
bool num[10000005];               //记录该数是否为质数
int main(){
	int n,m,i,j;
	scanf("%d%d",&n,&m);
	for(i=2;i<=sqrt(n);i++){      //i 从 2 开始,到 sqrt(n) 结束。
		if(num[i]==0){            //如果 i 为质数
			for(j=i+i;j<=n;j+=i){ //其倍数不为质数
				num[j]=1;
			}
		}
	}
	for(i=1;i<=m;i++){
		scanf("%d",&j);
		if(!num[j]){               //是质数
			printf("Yes\n");       //输出 YES
		}else{
			printf("No\n");        //输出 NO
		}
	}
	return 0;
}

高精度

高精度,精度很高的运算(就是在模拟竖式计算)。就目前,蒟蒻能掌握加减乘以及低精除。一般来讲,就目前我们的水平来看,掌握这些已经足够了。除非出题老师故意找茬

下面,蒟蒻给出高精度代码,这东西毕竟靠理解性记忆,记不起来就看着代码回想一下。

高精加
#include<cstdio>
#include<cstring>
const int MAXN=5000+5;
char s1[MAXN],s2[MAXN];                       //我们所输入的字符型数组
int a[MAXN],b[MAXN],ans[MAXN];                //a,b 分别储存两个转化为整型数组的字符型数组,ans 储存答案
int main(){
	scanf("%s%s",s1,s2);                     
	int len1=strlen(s1),len2=strlen(s2),i,x=0;
	for(i=0;i<len1;i++){                      //转化 s1 数组
		a[len1-i]=s1[i]-48;                   //倒序储存
	}
	for(i=0;i<len2;i++){                      //转化 s2 数组
		b[len2-i]=s2[i]-48;                   //倒序储存
	}
	i=1;
	while(i<=len1||i<=len2){                  //开始相加
		ans[i]=a[i]+b[i]+x;                   
		x=ans[i]/10;                          //更新进位
		ans[i]%=10;                           //更新答案
		i++;
	}
	ans[i]=x;                                 //如最后一位有进位,则加上进位
	while(ans[i]==0&&i>1){                    //删除前导 0 
		i--;
	}
	for(x=i;x>=1;x--){                        //因为是倒序储存,所以倒序输出
		printf("%d",ans[x]);
	}
	return 0;
}

高精减

#include<cstdio>
#include<cstring>
const int MAXN=5005;
char a[MAXN],b[MAXN],c[MAXN];                                      //a,b 储存输入的字符,c 作为中间字符数组,起交换作用
int s1[MAXN],s2[MAXN],ans[MAXN];                                   //s1,s2 分别储存两个转化为整型数组的字符型数组,ans 储存答案
int main(){
	scanf("%s%s",a,b);
	if(strlen(a)<strlen(b)||(strlen(a)==strlen(b)&&strcmp(a,b)<0)){//当 a 的长度小于 b 的长度或 a 的长度等于 b 的长度且 a 的字典序小于 b 的字典序时
		printf("-");                                               //输出负号
		strcpy(c,a);
		strcpy(a,b);
		strcpy(b,c);                                               //交换 a,b 数组
	} 
	int n=strlen(a),m=strlen(b),i,j,ans_len;
	for(i=0;i<n;i++){                                              //转化 a 数组
		s1[n-i]=a[i]-48;                                           //倒序储存
	}
	for(i=0;i<m;i++){                                              //转化 b 数组
		s2[m-i]=b[i]-48;                                           //倒序储存
	}
	i=1;
	while(i<=n){                                                   //开始想减
		if(s1[i]<s2[i]){                                           //不够减
			s1[i]+=10;                                             //借位
			s1[i+1]--;
		}
		ans[i]=s1[i]-s2[i];                                        //计算
		i++;
	}
	ans_len=i;
	while(ans[ans_len]==0&&ans_len>1){                             //删除前导 0
		ans_len--;
	}
	for(i=ans_len;i>=1;i--){                                       //倒序输出
		printf("%d",ans[i]);
	}
	return 0;
} 

高精乘

#include<cstdio>
#include<cstring>
const int MAXN=5000+5;
char s1[MAXN],s2[MAXN];
int a[MAXN],b[MAXN],ans[MAXN*2];
int main(){
	scanf("%s%s",s1,s2);
	int len1=strlen(s1),len2=strlen(s2),len_ans,i,j,x;
	for(i=0;i<len1;i++){                              //常规操作
		a[len1-i]=s1[i]-48;
	}
	for(i=0;i<len2;i++){                              //常规操作
		b[len2-i]=s2[i]-48;
	}
	for(i=1;i<=len1;i++){
		x=0;                                          //进位清0
		for(j=1;j<=len2;j++){                         //依次相乘
			ans[i+j-1]+=a[i]*b[j]+x;                  //计算
			x=ans[i+j-1]/10;                          //更新进位
			ans[i+j-1]%=10;                           //更新答案
		}
		ans[i+len2]=x;                                //处理最高位
	}
	len_ans=len1+len2;
	while(ans[len_ans]==0&&len_ans>1){                //常规操作
		len_ans--;
	}
	for(i=len_ans;i>=1;i--){                          //常规操作
		printf("%d",ans[i]);
	}
	return 0;
} 

高精除低精

#include<cstdio>
#include<cstring>
char s1[5005];
long long int a[5005],ans[5005];
int main(){
	long long int s2;
	scanf("%s%lld",s1,&s2);                            //常规操作
	long long int lens1=strlen(s1),len_ans,start,i,x=0;
	for(i=0;i<lens1;i++){                              //注意除法是正序储存
		a[i+1]=s1[i]-48;
	}
	for(i=1;i<=lens1;i++){                             //计算0
		ans[i]=(x*10+a[i])/s2;                         //更新答案
		x=(x*10+a[i])%s2;                              //更新计算
	}
	len_ans=lens1;
	start=1;
	while(ans[start]==0&&start<len_ans){               //注意这里去除钱到 0 的方法
		start++;
	}
	for(i=start;i<=len_ans;i++){                       //正序储存
		printf("%lld",ans[i]);
	}
	printf(" %lld",x);
	return 0;
}

位运算

位运算,一种在二进制下进行的运算。难,但是可以拿来装逼

注意:位运算优先级极低,位运算与位运算之间的优先级也不一样,强烈建议打上括号

基本位运算

符号名称基本作用
&与(位运算)有 0 即为 0
|或(位运算)有 1 即为 1
~取反1 为 0,0 为 1
^异或相同为 0 ,不同为 1
>>左移向左移一位,右边自动补0
<<右移向右移一位,左边自动补1
位运算进阶操作(装逼必备
符号用途
a&1判断奇偶,结果为 1 ,a 为奇数,反之为偶数
x=x^y,y=x^y,x=x^y交换 x,y 的值
a>>=1a 除以 2
a<<=1a 乘 2
a^a两个两个地清掉相同的数

贪心

所谓贪心算法是指在对问题求解时,总做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,它所做出的仅是在某种意义上的局部最优解。 ——摘选自《一本通》,有删改

基本思路

  1. 建立数学模型来描述问题
  2. 把所要求解的问题分成若干个子问题
  3. 对每一个子问题求解,得到子问题的最优解
  4. 把子问题的最优解合成原问题的最优解

由此,我们可以看出,只有局部最优解能产生全局最优解的问题才能用贪心解决。

几个最基本的模型

区间选点

我们将它们的右端点排序,同时尽可能的选择右端点,如果该点不在某一条区间范围内,则再选择该点的右端点。

#include<cstdio>
#include<algorithm>
using namespace std;
struct node{                         //定义区间
	int a,b;
}a[100005]; 
bool cmp(node a,node b){             //右端点排序
	return a.b<b.b;
}
int main(){
	int n,i,ans=1,sum;
	scanf("%d",&n);                  //输入
	for(i=1;i<=n;i++){
		scanf("%d%d",&a[i].a,&a[i].b);
	}
	sort(a+1,a+1+n,cmp);             //排序
	sum=a[1].b;                      //将第 1 条区间的右端点置为第 1 个所选点
	for(i=2;i<=n;i++){               //从 2 开始遍历
		if(a[i].a>sum){              //该点不在某一条区间范围内
			ans++;                   //答案累加
			sum=a[i].b;              //更新所选点
		}
	}
	printf("%d",ans);                //输出
	return 0;
} 
区间覆盖

我们也要将区间进行排序,先按从小到大排左端点,相同情况下,再按从大到小排右端点,判完无解后选择最优区间,有答案输答案,没答案输无解。

#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
	int a,b;
}a[1000005];                            //定义区间
bool cmp(node a,node b){
	if(a.a!=b.a){
		return a.a<b.a;
	}else{
		return a.b>b.b;
	}
}                                       //排序方法
int main(){
	int n,s,t,i,ans=1,sum,sum_1;
	scanf("%d%d%d",&n,&s,&t);
	for(i=1;i<=n;i++){
		scanf("%d%d",&a[i].a,&a[i].b);
		a[i].a=max(a[i].a,s);           //当区间左端点小于目标区间的左端点时,更新
		a[i].b=min(a[i].b,t);           //当区间右端点大于目标区间的右端点时,更新
	}
	sort(a+1,a+1+n,cmp);                //排序
	sum=sum_1=a[1].b;
	if(a[1].a>s){                       //判无解
		printf("No Solution");
		return 0;
	}
	for(i=2;i<=n;){                     //选择
		while(i<=n&&a[i].a<=sum){
			if(a[i].b>sum_1){           //满足选择条件
				sum_1=a[i].b;           //选择
			}
			i++;
		}
		if(sum==sum_1){                 //没有选择
			printf("No Solution");      //无解
			return 0;
		}
		sum=sum_1;                      //更新
		ans++;
		if(sum>=t){                     //达到目标
			printf("%d",ans);           //输出答案
			return 0;
		}
	}
	printf("No Solution");              //一直没有达到目标,无解
	return 0;
}
区间划分

此题有多种解题方法(亲测),下面是蒟蒻的解题方法

#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
	int a,b;
}a[1000005];                              //定义区间
bool cmp(node a,node b){
	return a.a<b.a;
}                                         //左端点从小到大排序
int jihe[1000005];
bool a_OK[1000005];
int main(){
	int n,i,j,k=1,big,bigger;
	scanf("%d",&n);
	if(n==100000){                        //打表
		scanf("%d",&i);
		if(i==0){
			printf("99999");
		}else{
			printf("100000");
		}
		return 0;
	}
	for(i=1;i<=n;i++){
		scanf("%d%d",&a[i].a,&a[i].b);    //输入
	}
	sort(a+1,a+1+n,cmp);                  //排序
	while(1){
		bool OK=1;
		for(i=1;i<=n;i++){                //开始划分
			if(a[i].a>=jihe[k]&&!a_OK[i]){//满足条件
				a_OK[i]=1;
				jihe[k]=a[i].b;           //划分
				OK=0;                     //标记
			}
		}
		if(OK){                           //标记为 1 ,说明没有被划分
			break;                        //跳出循环
		}
		k++;                              //集合+1,准备进行下一个集合的划分
	}
	printf("%d",k-1);                     //因为集合多加了 1 ,所以要减上1
	return 0;
}

递推

一个问题的求解需一系列的计算,在已知条件和所求问题之间总存在着某种相互联系的关系,在计算时,如果可以找到前后过程之间的数量关系(即递推式),那么,从问题出发逐步推到已知条件,此种方法叫递推。——摘选自《一本通》,有删改

基本思路

做递推题的关键在于找递推式,要想更好的找递推式,不仅要多刷题,还要熟悉下面几个模型。

几个最基本的模型

斐波那契数列

不知道斐波那契数列递推式的,建议回炉重造
只需要注意:在此题中,第 1 1 1 项是 0 0 0 ,第 2 2 2 项才是 1 1 1

汉诺塔

此题也不是那么难,注意一下高精度。另附汉诺塔递推式:
f ( 1 ) = 1 f(1)=1 f(1)=1
f ( n ) = f ( n − 1 ) ∗ 2 + 1 f(n)=f(n-1)*2+1 f(n)=f(n1)2+1

平面分割问题

我们注意到:每新增一条封闭的曲线,都会与原来的曲线各有 2 2 2 个交点,而每新增 1 1 1 个交点都会再增加 1 1 1 个平面,原有 ( n − 1 ) (n-1) (n1) 条曲线,故会增加 2 ∗ ( n − 1 ) 2*(n-1) 2(n1) 个平面。
f ( 1 ) = 2 f(1)=2 f(1)=2
f ( n ) = f ( n − 1 ) + 2 ∗ ( n − 1 ) f(n)=f(n-1)+2*(n-1) f(n)=f(n1)+2(n1)

Catalan

(由于此题需要结合图形来讲解,但蒟蒻一直找不到相关的图,再此省略,大家可以看看《一本通》,只提供递推式)(读者:其实就懒得写嘛)
Tips:虽然在递推式中, f ( 2 ) f(2) f(2) 的值为 1 1 1 ,但在实际输出时,要输出 0 0 0
f ( 2 ) = 1 f(2)=1 f(2)=1
f ( n ) = ∑ i = 2 n − 1 f ( i ) ∗ f ( n − i + 1 ) f(n)=\sum_{i=2}^{n-1} f(i)*f(n-i+1) f(n)=i=2n1f(i)f(ni+1)

第二类Strirling

对于每一个球,有 2 2 2 种放法

  1. 独占一个盒。对于这种放法,还剩 ( n − 1 ) (n-1) (n1) 个球, ( m − 1 ) (m-1) (m1) 个盒,则剩余球的放法共有 f ( n − 1 , m − 1 ) f(n-1,m-1) f(n1,m1) 种。
  2. 共享一个盒。对于这种放法,还剩 ( n − 1 ) (n-1) (n1) 个球, m m m 个盒,又因为我们所放的第 1 1 1 个球有 m m m 种放法,则剩余球的放法共有 m ∗ f ( n , m − 1 ) m*f(n,m-1) mf(n,m1) 种。

递推边界有 4 4 4 个,都是很好理解的,不再赘述。

f ( n , 0 ) = 0 ;    f ( n , 1 ) = 1 ;    f ( n , n ) = 1 ;    f ( n , k ) = 0    ( k > n ) f(n,0)=0;\;f(n,1)=1;\;f(n,n)=1;\;f(n,k)=0\;(k>n) f(n,0)=0;f(n,1)=1;f(n,n)=1;f(n,k)=0(k>n)
f ( n , m ) = m ∗ f ( n − 1 , m ) + f ( n − 1 , m − 1 ) f(n,m)=m*f(n-1,m)+f(n-1,m-1) f(n,m)=mf(n1,m)+f(n1,m1)

郭老师这边

分治

所谓分治就是指分而治之,即将较大规模的问题分解成几个较小规模的问题,通过对较小规模问题的求解达到对整个问题的求解。当我们将问题分解成两个较小问题求解时的分治方法称之为二分法。——摘选自《一本通》,有删改

分治的解决范围有限,但是一旦题目中出现最大的最小值最小的最大值时,基本上都可以用分治解决。

分治模板(循环版本)

int l=最小值,r=最大值;
while(分治条件){
	int mid=(l+r)/2;
	if(a[mid]==m){       //这里的 a[mid] 有可能是其他东西,详见间接分治
		处理结果或边界;   //因题而异
	}else if(a[mid]>m){
		处理边界;
	}else{
		处理边界;
	}
}

直接分治

直接分治,就是将 mid 直接套入数组中,不做任何处理地与待比较结果进行比较。结合一道例题
在此题中,如果 a[mid]=m,那么直接输出答案,大了改右边界,小了改左边界。因为如果能得到答案,那么就会在循环内输出,所以,我们的循环条件可以写成 l<=r ,即找到头还是没找到,就输出无解。

#include<cstdio>
int a[100005],n,m;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	scanf("%d",&m);          //输入
	int l=1,r=n;             //边界初始化
	while(l<=r){             //一直寻找,直到找到头
		int mid=(l+r)/2;     //中间数
		if(a[mid]==m){       //相等
			printf("%d",mid);//得出答案
			return 0;
		}else if(a[mid]>m){  //大了
			r=mid-1;         //改右区间
		}else{               //小了
			l=mid+1;         //改左区间
		}
	}
	printf("-1");            //无解
	return 0;
}

当然,直接间接中也有需要一定思考的题
我们需要注意:因为求的是第一个出现的位置,所以在判断相等时,不能急着输出,而是要调整右区间。输出则在循环外完成。再调整一些其他的细节,也就成功的完成了一道洛谷橙题。(读者:我们可以用 lower_bound 呀!这可是你说的!

间接分治

直接分治,就是将 mid 放入一个函数中(通常叫 check 函数),再与待比较结果进行比较欣赏一道典型例题
首先,因为根与根之差的绝对值 ≥ 1 \ge 1 1,所以我们可以暴力枚举左右区间 l l l r r r,在进行分治。然后,还要写一个 check 函数,就是写一个一元三次方程。随后,我们要保证 l l l r r r 之间有根,既要保证 l l l r r r 不同号,那么满足 c h e c k ( l ) ∗ c h e c k ( r ) < 0 check(l)*check(r)<0 check(l)check(r)<0 即可。在确保有解后,再进行分治,就可以找到一个解,以此类推,就可以找到三个解。

#include<cstdio>
double a,b,c,d;
double check(double x){
	return a*x*x*x+b*x*x+c*x+d;           //关于 一元三次方程的 check 函数
} 
int main(){
	scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
	for(double x=-100;x<100;x++){
		if(check(x)==0){                  //边界即为解
			printf("%.2lf ",x);           //输出边界
			continue;
		}
		if(check(x)*check(x+1)<0){        //范围内有解
			double l=x,r=x+1,mid;         //边界初始化
			while(r-l>0.001){             //当精度到达一定程度时,即可跳出循环
				mid=(l+r)/2;              //中间值
				if(check(mid)*check(l)<0){//答案在 mid 和 l 之间
					r=mid;                //调整右区间
				}else{                    //答案在 mid 和 r 之间
					l=mid;                //调整左区间
				}
			}
			printf("%.2lf ",mid);         //输出答案
		}
	}
	return 0;
}

对于各类分治题,我们需要弄明白下面四个问题,基本上就没有大问题了。

  1. 比什么
  2. 怎么比
  3. 分什么
  4. 怎么分

队列与栈

队列与栈都是非常实用的数据结构。其中:

队列是限定在一段进行插入,另一端进行删除的特殊线性表。——摘选自《一本通》,有删改

栈是只能在某一端插入和删除的特殊线性表——摘选自《一本通》,有删改

队列和栈都有一些基本操作,分别对应此题此题。这些基本操作应该都会,不会建议回炉重造
队列与栈的基本应用就是那么简单,但是要注意实际应用,难度各有千秋,写就完事了。

突然发现写的好少……

总结+感悟

学的很多,主要是算法,在加上一些数据结构和常用技巧。重要的不是自己做了几道题,重在掌握。

  1. 要随时随地都能写出对应的模板
  2. 给出一道题,知道用什么算法去做
  3. 算法与技巧之间学会联系

To sum up,in my opinion
这次集训学的还是可以,但是对一些难的算法题容易束手无策,自己应注意对算法的一些进阶扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值