蓝桥杯预习

刷点经典题,难题固然要看,但蓝桥杯偏基础。

hdu 4489

知识点:记忆化搜索

1、这题中,高和矮是相对概念,对称,所以除了1这个特判,其他dfs一次然后乘二即可。
2、从0位,或从第n位开始都一样,但要注意的是,你从第0位,而且最小值选的不是0,而是-1时,会出现dp[0][-1][0]的情况,这涉及C++的变量的基本存储情况,它不会初始化为-1,而是变成sum,或cnt,或其他的值,其他初始为0,所以你如果照我下面代码遍历时改成dfs(0,-1,0),结果始终为0.
3.也可以用排列组合做。但重点还是回顾一下这种题型。

#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=1e5+7;
int n,m,T;
ll dp[30][30][2],sum,cnt;
bool f[30];
ll dfs(int pos,int pre,bool t){
	if(pos==n)	return 1;
	if(dp[pos][pre][t]!=-1){
		cnt++;		//看节省了多少次遍历 
		return dp[pos][pre][t];
	}
	ll ans=0;
	for(int i=1;i<=n;i++){
		if(f[i])	continue;
		if(t&&pre>i){
			f[i]=1;
			ans+=dfs(pos+1,i,0);
			f[i]=0;
		}
		else if(!t&&pre<i){
			f[i]=1;
			ans+=dfs(pos+1,i,1);
			f[i]=0;
		}
	}
	return dp[pos][pre][t]=ans;
}
int main(){
	cin>>T;
	while(T--){
		cin>>m>>n;	sum=cnt=0;
		memset(dp,-1,sizeof(dp));	memset(f,0,sizeof(f));
		if(n==1){
			printf("%d 1\n",m);
			continue;
		}
		sum=(ll)dfs(0,0,0)*2;
		printf("%d %lld\n",m,sum);
//		cout<<cnt<<endl<<endl;
	}
}

/*
4
1 1
2 3
3 4
4 20

1 1
2 4
3 10
4 740742376475050
*/

hdu-5875

知识点:单调栈

题目大意:
    给定一个数组,每次查询区间[l, r],
    输出A[l] % A[l+1] % ... % A[r]
    
单调栈在这里的应用:查找区间任一个数字右边第一个比它小的数字

分析:
一开始数字为a[l],然后依次对a[l+1]到a[r]取模,即连续取模。
一个个暴力绝对T,但可以发现当前数字只会后面出现比它小的时才会改变,所以我们可以跳着取模。
线段树找右边第一小比较繁琐,但可以用单调栈。

复杂度:
预处理O(n),m次询问,理论上O(n+m),实际最坏情况下可以达到O(m*n*n),还好这题没卡数据
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=1e5+7;
int n,T,m,a[maxn],f[maxn],x,y,sum;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int main(){
	T=read();
	while(T--){
		n=read();
		for(int i=1;i<=n;i++)	a[i]=read();
		stack<int>s;
		for(int i=n;i>0;i--){
			while(!s.empty()&&a[s.top()]>a[i])	s.pop();
			if(s.empty())	f[i]=-1;
			else	f[i]=s.top();
			s.push(i);
		}
		m=read();
		while(m--){
			x=read();	y=read();
			if(x==y){
				printf("%d\n",a[x]);
				continue;
			}
			sum=a[x];
			while(1){
				int id=f[x];
				if(id==-1||id>y)	break;
				sum%=a[id];
				x=id;
			}
			printf("%d\n",sum);
		}
	}
}

/*
1
3
2 3 3
1
1 3

2
*/
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值