HDNOIP普及+提高整合

好久没有更新博客了。。。这几天一直在切 HDNOIP。。。

正式时跪惨了。。。所以考完后回来奋力切出了所有题。

 

[COJ3351]HDNOIP201601回文质数

试题描述

回文数是具有对称性的自然数,例如 25352 和 1881 都是回文数。在一个完全由数字符 组成的字符串中任意取一个长度 L 的子字符串,都可以转化为一个不超过 L 位的整数,如果 此整数是质数,我们就把这个子串称为回文质数串。 
你的任务是在一个完全由数字符组成的字符串 n 中找出第一个(位置最靠前的一个)长 度 L 的回文质数子串。

输入

先是 n,后是 L,中间用空格分开。 

输出

只有一项输出结果。如果 n 中存在符合条件的子串就输出这个子串,否则输出 n 中长度 L 的回文子串个数。 

输入示例

12301101113 3

输出示例

101

数据规模及约定

70%的数据满足 l<7。 
100%的数据满足 0<l<=10;n 的位数不小于 l 且不大于 100。 

题解

暴力。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 110
#define LL long long
int n, k, ans;
LL ten[12];
char S[maxn];

bool judge(LL x) {
	int num[12], cnt = 0;
	LL t = x;
	while(x) {
		num[++cnt] = x % 10;
		x /= 10;
	}
	while(cnt < k) num[++cnt] = 0;
	for(int i = 1; i <= cnt; i++) if(num[i] != num[cnt-i+1])
		return 0;
	ans++;
	if(t == 1) return 0;
	int m = (int)sqrt((double)t + .5);
	for(int i = 2; i <= m; i++) if(t % i == 0)
		return 0;
	return 1;
}

int main() {
	scanf("%s", S + 1); n = strlen(S + 1);
	k = read();
	
	LL sum = 0;
	ten[0] = 1;
	for(int i = 1; i <= k; i++) ten[i] = ten[i-1] * 10;
	for(int i = 1; i <= k; i++) sum = sum * 10 + S[i] - '0';
	if(judge(sum)) return printf("%lld\n", sum), 0;
	for(int i = k + 1; i <= n; i++) {
		sum = (sum - ten[k-1] * (S[i-k] - '0')) * 10 + S[i] - '0';
		if(judge(sum)) return printf("%lld\n", sum), 0;
	}
	printf("%d\n", ans);
	
	return 0;
}

 

[COJ3352]HDNOIP201602贪吃的宝宝

试题描述

宝宝每天要吃一包零食,借美食购物节的机会,他让妈妈领着买回来 n 包零食。食品的 包装上都标明了此食品的保质期,妈妈也还记得每包食品的价格。出于对宝宝健康的考虑, 妈妈规定宝宝每天吃零食不得超过一包且不准吃过期食品。 

怎样安排宝宝吃零食的方案才能使浪费(无法吃上)的零食总价最少呢?你能帮宝宝找 到最小浪费总价吗?

输入

第一行是 n,接下来的 n 行每行两个整数,先是保质期,后是价格。保质期是从现在算 起的保质天数。 

输出

一个数,表示浪费食品的最小总价。 

输入示例

4
2 4
5 1
1 3
2 2

输出示例

2

数据规模及约定

60%的数据满足 n<10。 
80%的数据满足 n<=1000,保质期不超 100000。 
100%的数据满足 n<100000,且保质期<2^31,每包零食价格不超 2^15。 

题解

贪心。按照保质期从大到小排序,那么对于第 i 个食物,可以选择它前面的所有食物,可选择的个数为当前时刻与最大时刻(即第一个食物的保质期)之差,用个堆以价值为关键字存下所有食物,贪心地取即可。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 100010
#define LL long long
int n;
struct Snack {
	int d, v;
	Snack() {}
	Snack(int _, int __): d(_), v(__) {}
	bool operator < (const Snack& t) const { return v < t.v; }
} ss[maxn];
bool cmp(Snack a, Snack b) { return a.d > b.d; }

priority_queue <Snack> Q;
int main() {
	n = read();
	for(int i = 1; i <= n; i++) {
		int a = read(), b = read();
		ss[i] = Snack(a, b);
	}
	
	sort(ss + 1, ss + n + 1, cmp);
	LL ans = 0, sum = 0;
	for(int i = 1; i <= n; i++) {
		if(i > 1 && ss[i].d != ss[i-1].d) {
			int x = ss[i-1].d - ss[i].d;
			while(!Q.empty() && x--) ans += Q.top().v, Q.pop();
		}
		Q.push(ss[i]); sum += ss[i].v;
	}
	int x = ss[n].d;
	while(!Q.empty() && x--) ans += Q.top().v, Q.pop();
	
	printf("%lld\n", sum - ans);
	
	return 0;
}

 

[COJ3353]HDNOIP201603紧急使命

试题描述

在久远以前的部落年代,s 部落因紧急事务要派人去往 t 部落。出于贪心,各部落分别 规定了对进入或路经本部落的人强制收费的额度,如果存在 s 到 t 的直达道路,使者只需缴 纳 t 部落的费用,否则还要向途经的各部落交“买路钱”。 部落编号从 1 到 n。题目数据除了 n、s、t 等信息外,还有 s 部落酋长给使者的钱数 k 以及各部落间的相邻关系信息。 

相邻关系信息包括两个部落间直接互通道路的条数 m,还有每条道路所连两个部落的编 号及通过此道路所需的时间。 你的任务是由这些信息求出在钱数 k 的限制内,s 部落使者最少要花多少时间才能到达 t 部落?若没有 s 部落通向 t 部落的路径或每条能走的路径收费都超 k,就输出-1。 

输入

第一行依次是 n、m、k、s、t,共 5 个正整数。 
第二行共有 n 个非负整数 ki,依次是 1、2……n 号部落的收费价格。 
接下来的 m 行每行 3 个整数 a、b、c,表示 a 部落与 b 部落之间有一条不经过其他部落 的双向道路,需要花长度 c 的时间。 注意,a 部落与 b 部落间直接互通的道路可能不止一条。

输出

只有一项输出内容。 
若在 k 的钱数内能从 s 到达 t,输出的是其中最快方案的时间数,否则输出-1,表示无 法到达 t。

输入示例

5 6 10 1 5
7 2 3 8 4
2 1 3
1 4 2
3 2 5
4 5 1
3 4 2
5 3 7

输出示例

15

数据规模及约定

50%的数据满足 2<n<=10 且 k<100; 
70%的数据满足 2<n<20 且 k<=100。 
100%的数据满足 2<n<=100,0<k<10000,1<m<n^2,s≠t,0<s, t<=n。 
每条道路满足:0<a,b<=n 且 0<c<=1000。 

题解

最短路。我拒绝写 SPFA。。。鬼知道有没有网格图。。。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 110
#define maxm 20010
#define maxk 10010
#define oo 2147483647
int n, m, k, s, t, val[maxn], head[maxn], next[maxm], to[maxm], dist[maxm];

void AddEdge(int a, int b, int c) {
	to[++m] = b; dist[m] = c; next[m] = head[a]; head[a] = m;
	swap(a, b);
	to[++m] = b; dist[m] = c; next[m] = head[a]; head[a] = m;
	return ;
}

struct Node {
	int x, y, d;
	Node() {}
	Node(int _1, int _2, int _3): x(_1), y(_2), d(_3) {}
	bool operator < (const Node& t) const { return d > t.d; }
} ;
priority_queue <Node> Q;
bool vis[maxn][maxk];
int d[maxn][maxk];
void Dijkstra() {
	for(int i = 1; i <= n; i++)
		for(int j = 0; j <= k; j++) d[i][j] = oo;
	d[s][0] = 0;
	Q.push(Node(s, 0, 0));
	while(!Q.empty()) {
		Node u = Q.top(); Q.pop();
		if(vis[u.x][u.y]) continue;
		vis[u.x][u.y] = 1;
		for(int e = head[u.x]; e; e = next[e]) {
			Node v(to[e], u.y + val[to[e]], 0);
			if(u.y + val[to[e]] <= k && d[v.x][v.y] > d[u.x][u.y] + dist[e]) {
				d[v.x][v.y] = d[u.x][u.y] + dist[e];
				if(!vis[v.x][v.y]) Q.push(Node(v.x, v.y, d[v.x][v.y]));
			}
		}
	}
	return ;
}

int main() {
	n = read(); int m = read(); k = read(); s = read(); t = read();
	for(int i = 1; i <= n; i++) val[i] = read();
	for(int i = 1; i <= m; i++) {
		int a = read(), b = read(), c = read();
		AddEdge(a, b, c);
	}
	
	Dijkstra();
	
	int ans = oo;
	for(int i = 0; i <= k; i++) ans = min(ans, d[t][i]);
	
	printf("%d\n", ans < oo ? ans : -1);
	
	return 0;
}

 

[COJ3348]HDNOIP201604打电话

试题描述

小松鼠每天晚上都会给一位同学打电话,并且总要问一些类似的问题,结果有时候就会使得对方有些不耐烦。

今天晚上,他又要开始打电话了,这次他有n种问题可问,其中第i种问题他最多会问ti次。他发现,同一种问题问了至少k次以后,对方就会对这问题不耐烦。他想知道,问了q问题之后,对方至少会对多少问题不耐烦。

输入

第一行三个整数n、k、q。第二行n个整数t1, t2, …, tn。

输出

一个数,对方至少会对多少种问题不耐烦。

输入示例

8 5 36
6 9 10 2 8 4 1 7

输出示例

2

数据规模及约定

30%的数据中,n = 5,1 ≤ ti ≤ 30 
100%的数据中,1 ≤ n,k ≤ 100000,1 ≤ ti ≤ 10000,1 ≤ q ≤ t1 + t2 + ⋯+ tn 

题解

还是贪心。先给所有的 t 减去 k-1 次,若不够则减到 0 为止,然后从大到小依次将所有的 t 减到 0;q 随之减小到 0 为止。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 100010
int n, k, q, A[maxn], B[maxn], cnt;

int main() {
	n = read(); k = read(); q = read();
	for(int i = 1; i <= n; i++) A[i] = read();
	
	sort(A + 1, A + n + 1);
	for(int i = 1; i <= n; i++)
		if(A[i] > k - 1) B[++cnt] = A[i] - k + 1, q -= k - 1;
		else q -= A[i];
	if(q <= 0) return puts("0"), 0;
	for(int i = cnt; i; i--)
		if(q > B[i]) q -= B[i];
		else return printf("%d\n", cnt - i + 1), 0;
	
	return 0;
}

 

[COJ3349]HDNOIP201605旅行路线

试题描述

落选UOI 地球代表队之后,小松鼠跑到了一个奇怪的地方玩耍。 
这里有 n 个村庄和 n – 1 条道路,任何两个村庄之间都有唯一的简单路径相连通,小松鼠 每天晚上都会选择一个村庄住下,并且不会在同一个村庄住下两次。而每一个白天,他会沿着某两个村庄之间唯一的简单路径,从前一天晚上所在的村庄抵达另一个村庄,并在那里住 一个晚上。 
小松鼠希望能把路上的风景看个够,所以需要一个在村庄住下的顺序,使得经过的总路 程最长。

输入

第一行一个整数 n。下面n − 1行,每行有三个整数 a、b、c 分别表示一条道路连接的两 个村庄各自的编号 a、b,和这条道路的长度 c。 

输出

一个数,最长的总路程。

输入示例

6
2 1 3
3 2 2
4 5 2
4 6 5
2 4 4

输出示例

44

数据规模及约定

测试点 0~5,1 ≤ n ≤ 10 
测试点 6~10,任何一个村庄最多与两条道路相连,1 ≤ n ≤ 100000,c = 1 
测试点 11~13,任何一个村庄最多与两条道路相连,1 ≤ n ≤ 100000,1 ≤ c ≤ 100000 
测试点 14~17,1 ≤ n ≤ 100000,c = 1 
测试点 18~19,1 ≤ n ≤ 100000,1 ≤ c ≤ 100000 

题解

找出树的重心,以重心为根,处理出所有的 d[i] 表示节点 i(除重心外)到根节点的距离。若只有一个重心,则答案为 2∑d[i] - min{ d[i] };若有两个重心,则答案为 2∑d[i] - 两个重心之间的距离。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 100010
#define maxm 200010
#define oo 2147483647
#define LL long long
int n, m, head[maxn], next[maxm], to[maxm], dist[maxm];

void AddEdge(int a, int b, int c) {
	to[++m] = b; dist[m] = c; next[m] = head[a]; head[a] = m;
	swap(a, b);
	to[++m] = b; dist[m] = c; next[m] = head[a]; head[a] = m;
	return ;
}

int root, size, f[maxn], siz[maxn], r2;
bool fl;
void getroot(int u, int fa) {
	siz[u] = 1; f[u] = 0;
	for(int e = head[u]; e; e = next[e]) if(to[e] != fa) {
		getroot(to[e], u);
		siz[u] += siz[to[e]];
		f[u] = max(f[u], siz[to[e]]);
	}
	f[u] = max(f[u], size - siz[u]);
	if(f[root] > f[u]) root = u, fl = 0;
	else if(f[root] == f[u]) fl = 1, r2 = u;
	return ;
}

LL dep[maxn], sum;
void build(int u, int fa) {
	sum += dep[u];
	for(int e = head[u]; e; e = next[e]) if(to[e] != fa) {
		dep[to[e]] = dep[u] + dist[e];
		build(to[e], u);
	}
	return ;
}

int main() {
	n = read();
	for(int i = 1; i < n; i++) {
		int a = read(), b = read(), c = read();
		AddEdge(a, b, c);
	}
	
	root = 0; f[0] = n + 1; size = n; getroot(1, 0);
	build(root, 0);
	sum <<= 1;
	if(!fl) {
		int mn = oo;
		for(int e = head[root]; e; e = next[e]) mn = min(mn, dist[e]);
		sum -= mn;
	}
	else
		for(int e = head[root]; e; e = next[e]) if(to[e] == r2)
			sum -= dist[e];
	
	printf("%lld\n", sum);
	
	return 0;
}

大胆猜想,不用证明

 

[COJ3350]HDNOIP201606弹飞松子

试题描述

小松鼠总喜欢让身边的所有物品都变得弹弹的。 
他刚刚在地上沿直线摆放了 n 个弹簧,分别位于第 0、1、……、n – 1 个位置上。第 i 个位置上的弹簧有初始弹性di,表示落在这个弹簧上的物品将会被向后弹到第i + di个位置上。 小松鼠可以修改每个弹簧的弹性,每个弹簧有被修改的难度系数ai,表示如果将这个弹簧的 弹性修改为di′,需要消耗|di − di′| ∙ ai的代价,但弹簧是不能向前弹的。 这时,一个松子从天而降,恰好落在了第 0 个位置上。小松鼠知道,松子在第 i 个位置 上的弹簧上弹一下,他就能获得bi的收益。他希望松子最后能落在第 n 个位置上,并且获得 的总收益减去总代价最大。

输入

第一行一个整数 n。下面 n 行,每行三个整数di、ai、bi。 

输出

一个整数,最大的总收益减总代价的值

输入示例

9
1 5 8
2 5 8
3 4 5
5 2 6
1 1 1
4 3 7
3 5 3
4 4 9
5 4 5

输出示例

23

数据规模及约定

30%的数据中,1 ≤ n ≤ 20 
70%的数据中,1 ≤ n ≤ 1000 另有 20%的数据中,ai = 1 
100%的数据中,1 ≤ n ≤ 60000,1 ≤ di ≤ n,1 ≤ ai ≤ 100,1 ≤ bi ≤ 5000 

题解

首先会想到斜率优化dp,于是推一波式子(令 Di = di + i)——

我们发现无法 O(n) 维护凸壳,原因有:

1.) aj 不单调

2.) 需要分类讨论

第 2 点很容易解决,我们可以分开处理,即分成两个坐标系,一个用来维护 i ≤ Dj 的情况,另一个维护 i ≥ Dj 的情况。

第 1 点也不难解决,因为 a 的范围只有 100,这就意味着每个决策点的横坐标只有 100 种可能,于是我们可以分别维护 100 个横坐标当前能够取到的最优决策点,查询的时候分别在这 100 种横坐标中找就好了。

当计算出 f(i) 时,当前决策点就产生了,我们向两个坐标系中分别插入这个决策点。其中,第一个坐标系中的每个决策点有一个“开始时间”属性,即当到达该时刻时即可使用这个决策点;第二个坐标系中每个决策点有一个“结束时间”属性,即到达这个时刻后就不能使用该决策点了(当前时刻时使用它的最后一时刻)。而以上所说的“开始、结束时间”就是 Di(想一想,为什么),前者可以用数组维护(设 F[x][t] 表示横坐标为 x,开始时间为 t 的最优决策点,即该点的 f(i)+Di·ai 最大),后者可以用堆维护(每个节点记录权值,即 f(i)-Di·ai 的值,以及结束时间,以权值为关键字建立堆)。

时间复杂度 O(100n + nlogn)(想一想,为什么)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <queue>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 60010
#define maxa 110
#define oo 1000000000
int n, d[maxn], a[maxn], b[maxn], f[maxn];
bool vP1[maxa][maxn];
struct Node {
	int val, pos;
	Node() {}
	Node(int _1, int _2): val(_1), pos(_2) {}
} P1[maxa][maxn], mx1[maxa];
struct Node2 {
	int val, end, pos;
	Node2() {}
	Node2(int _1, int _2, int _3): val(_1), end(_2), pos(_3) {}
	bool operator < (const Node2& t) const { return val < t.val; }
} ;
priority_queue <Node2> mx2[maxa];

int main() {
	n = read();
	for(int i = 0; i < n; i++) {
		d[i] = read() + i; a[i] = read(); b[i] = read();
	}
	
	for(int i = 0; i <= 100; i++) mx1[i] = Node(-oo, 0);
	f[0] = b[0];
	P1[a[0]][d[0]] = Node(f[0] + d[0] * a[0], 0); vP1[a[0]][d[0]] = 1;
	if(d[0] <= 0) mx1[a[0]] = Node(f[0] + d[0] * a[0], 0);
	mx2[a[0]].push(Node2(f[0] - d[0] * a[0], d[0], 0));
	for(int i = 1; i <= n; i++) {
		int mx, mxp; f[i] = -oo;
		for(int j = 0; j <= 100; j++) if(vP1[j][i] && P1[j][i].val > mx1[j].val)
			mx1[j] = P1[j][i];
		for(int j = 0; j <= 100; j++) if(mx1[j].val > -oo)
			mx = mx1[j].val, mxp = mx1[j].pos, f[i] = max(f[i], mx - i * j + b[i]);
		for(int j = 0; j <= 100; j++) if(!mx2[j].empty()) {
			Node2 u = mx2[j].top();
			while(u.end < i && !mx2[j].empty()) mx2[j].pop(), u = mx2[j].top();
			if(u.end >= i) {
				mx = u.val; mxp = u.pos;
				f[i] = max(f[i], mx + i * j + b[i]);
			}
		}
		Node v = Node(f[i] + d[i] * a[i], i);
		if(!vP1[a[i]][d[i]]) vP1[a[i]][d[i]] = 1, P1[a[i]][d[i]] = v;
		else if(P1[a[i]][d[i]].val < f[i] + d[i] * a[i]) P1[a[i]][d[i]] = v;
		mx2[a[i]].push(Node2(f[i] - d[i] * a[i], d[i], i));
//		printf("%d ", f[i]);
	}
	
	printf("%d\n", f[n]);
	
	return 0;
}

 

写个全套题解真不容易。。。。。

posted @ 2016-09-24 17:06  xjr01  阅读(486)  评论(0编辑  收藏  举报