NOIP 2018 提高组 真题

在这里插入图片描述

D1T1铺设道路

时间限制: 1 s 1s 1s
空间限制: 512 M B 512MB 512MB

题目描述

春春是一名道路工程师,负责铺设一条长度为 n 的道路。
铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 n n n 块首尾相连的区域,一开始,第 i i i 块区域下陷的深度为 d i di di
春春每天可以选择一段连续区间 [ L , R ] [L,R] [L,R],填充这段区间中的每块区域,让其下陷深度减少 1 1 1
在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 0 0 0
春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 0 0 0

输入格式

输入文件包含两行,第一行包含一个整数 n n n,表示道路的长度
第二行包含 n n n 个整数,相邻两数间用一个空格隔开,第 i i i 个整数为 d i di di

输出格式

输出文件仅包含一个整数,即最少需要多少天才能完成任务。

样例数据
input
6   
4 3 2 5 3 5
output
9
样例说明

一种可行的最佳方案是,依次选择: [ 1 , 6 ] 、 [ 1 , 6 ] 、 [ 1 , 2 ] 、 [ 1 , 1 ] 、 [ 4 , 6 ] 、 [ 4 , 4 ] 、 [ 4 , 4 ] 、 [ 6 , 6 ] 、 [ 6 , 6 ] [1,6]、[1,6]、[1,2]、[1,1]、[4,6]、[4,4]、[4,4]、[6,6]、[6,6] [1,6][1,6][1,2][1,1][4,6][4,4][4,4][6,6][6,6]

数据规模与约定

对于 30 30 30% 的数据, 1 ≤ n ≤ 10 1 ≤ n ≤ 10 1n10
对于 70 70 70% 的数据, 1 ≤ n ≤ 1000 1 ≤ n ≤ 1000 1n1000
对于 100 100 100% 的数据, 1 ≤ n ≤ 100000 , 0 ≤ d i ≤ 100001 ≤ n ≤ 100000 , 0 ≤ d i ≤ 10000 1≤n≤100000,0≤di≤100001≤n≤100000,0≤di≤10000 1n100000,0di100001n100000,0di10000在这里插入图片描述
单调队列维护即可
在这里插入图片描述

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int n, ans, tot;
int a[100010], q[100010];

inline void init() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
}

inline void work() {
	q[++tot] = a[1];
	for(int i = 2; i <= n; i++){
		if(a[i] <= q[tot] && tot) ans += q[tot--]-a[i];
		while(a[i] <= q[tot] && tot) tot--;
		q[++tot] = a[i];
	}
}

inline void outo() {
	printf("%d", ans+q[tot]);
}
int main() {
	init();
	work();
	outo();
	return 0;
}

在这里插入图片描述
在这里插入图片描述

D1T2货币系统

时间限制: 1 s 1s 1s
空间限制: 512 M B 512MB 512MB

题目描述

在网友的国度中共有 n n n 种不同面额的货币,第 i i i 种货币的面额为 a [ i ] a[i] a[i],你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 n n n、面额数组为 a [ 1.. n ] a[1..n] a[1..n] 的货币系统记作 ( n , a ) (n,a) (n,a)
在一个完善的货币系统中,每一个非负整数的金额 x x x 都应该可以被表示出,即对每一个非负整数 x x x,都存在 n 个非负整数 t [ i ] t[i] t[i] 满足 a [ i ] × t [ i ] a[i]×t[i] a[i]×t[i] 的和为 x x x。然而, 在网友的国度中,货币系统可能是不完善的,即可能存在金额 x x x 不能被该货币系统表示出。例如在货币系统 n = 3 , a = [ 2 , 5 , 9 ] n=3, a=[2,5,9] n=3,a=[2,5,9]中,金额 1 , 3 1,3 1,3 就无法被表示出来。
两个货币系统 ( n , a ) (n,a) (n,a) ( m , b ) (m,b) (m,b) 是等价的,当且仅当对于任意非负整数 x x x,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。
现在网友们打算简化一下货币系统。他们希望找到一个货币系统 ( m , b ) (m,b) (m,b),满足 ( m , b ) (m,b) (m,b) 与原来的货币系统 ( n , a ) (n,a) (n,a) 等价,且 m m m尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 m m m

输入格式

输入文件的第一行包含一个整数 T T T,表示数据的组数。
接下来按照如下格式分别给出 T T T 组数据。 每组数据的第一行包含一个正整数 n n n
接下来一行包含 n n n 个由空格隔开的正整数 a [ i ] a[i] a[i]

输出格式

输出文件共有 T T T 行,对于每组数据,输出一行一个正整数,表示所有与 ( n , a ) (n,a) (n,a)等价的货币系统 ( m , b ) (m,b) (m,b) 中,最小的 m m m

样例数据
input
2 
4 
3 19 10 6 
5 
11 29 13 19 17
output
2
5
样例说明

在第一组数据中,货币系统 ( 2 , [ 3 , 10 ] ) (2, [3,10]) (2,[3,10]) 和给出的货币系统 ( n , a ) (n, a) (n,a) 等价,并可以验证不存在 m < 2 m < 2 m2 的等价的货币系统,因此答案为 2 2 2。 在第二组数据中,可以验证不存在 m < n m < n mn的等价的货币系统,因此答案为 5 5 5

数据规模与约定

在这里插入图片描述
对于 100 100 100% 的数据,满足 1 ≤ T ≤ 20 , n , a [ i ] ≥ 1 1 ≤ T ≤ 20, n,a[i] ≥ 1 1T20,n,a[i]1
在这里插入图片描述
由题意可推,b必为a的子集
因为要使b中的元素最少,我们可以想象成b先复制一份a数组,然后再去掉一些并非必要的数即可
什么是并非必要的数呢?如果a集合中的某一个数可以由a集合中另外的数组成,这个数就不是必要的数
在这里插入图片描述

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int n, a[105], f[25005], ans, T;

inline void init() {
	scanf("%d", &n);
	memset(f, 0, sizeof f);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		f[a[i]] = 2;//2表示a集合原先存在的数
	}
	sort(a+1, a+1+n);
}

inline void work() {
	ans = 0;
	for(int i = 1; i <= a[n]; i++) 
		for(int j = 1; j <= n && f[i]; j++)
			if(i+a[j] <= a[n]) f[a[j]+i] = 1;//1表示可以拼凑出来的数
	for(int i = 1; i <= n; i++)
		if(f[a[i]] == 2) ans++;//原a集合中有多少个数不能被拼凑,就是必要的数
}

inline void outo() {
	printf("%d\n", ans);
}
int main() {
    scanf("%d", &T);
    while(T--) {
    	init();
		work();
		outo();
    }
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 5e3+10;
int n, m, len, dep, xx, yy;
int lin[N], ans[N], res[N];
bool vis[N];
vector<int> vec[N];
struct psx{int next, x, y;}e[N<<1];

inline void insert(int x, int y) {
	e[++len].next = lin[x];
	e[len].x = x;
	e[len].y = y;
	lin[x] = len;
}

inline void init() {
	scanf("%d%d", &n, &m);
	for(int i = 1, v, u; i <= m; i++) {
		scanf("%d%d", &u, &v);
		insert(u, v);
		insert(v, u);
		vec[v].push_back(u);
		vec[u].push_back(v);
	}
	for(int i = 1; i <= n; i++)
		sort(vec[i].begin(), vec[i].end());
}

inline void dfs1(int x, int fa) {
	if(vis[x]) return;
	vis[x] = 1;
	ans[++dep] = x;
	for(int i = 0; i < vec[x].size(); i++) {
		int y = vec[x][i];
		if(y == fa) continue;
		dfs1(y, x);
	}
}

inline void dfs2(int x, int fa) {
	if(vis[x]) return; 
	vis[x] = 1;
	res[++dep] = x;
	for(int i = 0; i < vec[x].size(); i++) {
		int y = vec[x][i];
		if(y == fa) continue;
		if((y==xx && x==yy) || (y==yy && x==xx)) continue;
		dfs2(y, x);
	}
}

inline bool check() {
	for(int i = 1; i <= n; i++) {
		if(res[i] < ans[i]) return 1;
		if(res[i] > ans[i]) return 0;
	}
}

inline void change() {
	for(int i = 1; i <= n; i++)
		ans[i] = res[i];
}

inline void work() {
	if(m == n-1) {
		dep = 0;
		dfs1(1, 0);
		return;
	}//如果是一棵树
	for(int i = 1; i <= len; i += 2) {
		dep = 0;
		xx = e[i].x;
		yy = e[i].y;//记录删去边的信息
		memset(vis, 0, sizeof vis);
		dfs2(1, 0);
		if(dep < n) continue;
		if(!ans[1]) change();
		else if(check()) change();//更新答案
	}//基环树,每次枚举删去其中一条边,选择字典序最小的方案
}

inline void outo() {
	for(int i = 1; i <= n; i++)
		printf("%d ", ans[i]);
}
int main() {
	init();
	work();
	outo();
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

65分部分分
#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int mod = 1e9+7;
int n, m;
inline void init() {
	scanf("%d%d",&n,&m);
}
inline int P(int a,int b) {
	int c = 1;
	while(b){
		if(b&1) c = 1LL*c*a%mod;
		a = 1LL*a*a%mod;
		b >>= 1;
	} 
	return c;
}
inline void work() {
	if(n == 2) {printf("%d\n",4LL*P(3,m-1)%mod);return;};
	if(n == 3)  {
		if(m == 1) puts("8");
		else if (m == 2) puts("36");
		else if (m == 3) puts("112");
		else printf("%d\n",112LL*P(3,m-3)%mod);
		return;
	}
}

int main() {
	init();
	work();
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值