CCPC2018-湖南全国邀请赛-重现赛 题解

A题 Easy h-index

A题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6276
题意: 这题意一开始没有快速理解,wa了两发才明白啥意思。可以理解为,题目给定一个 N N N,在给出 a 0 , a 1 , a 2 . . . a n a_0,a_1,a_2...a_n a0,a1,a2...an,表示权值为 i i i的物品数量有 a i a_i ai件,求最大的 h h h,其中 h h h满足权值大于 h h h的物品数量也大于 h h h
题解: 要注意一个坑点,权值大于 i i i的物品数量也可以算在 a i a_i ai当中,所以解法很简单,就是求一个后缀和,然后取 a i a_i ai i i i中小的那个即可。
代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
const int MAX = 2e5 + 10;
ll a[MAX];
int main() {
	ll n;
	while (~scanf("%lld", &n)) {
		ll maxx = 0;
		for (int i = 0; i <= n; i++)
			scanf("%lld", &a[i]);
		maxx = max(maxx, min(a[n], n));
		for (ll i = n - 1; i >= 0; i--) {
			a[i] = a[i + 1] + a[i];
			maxx = max(maxx, min(a[i], i));
		}
		printf("%lld\n", maxx);
	}
	return 0;
}

B题 Higher h-index

B题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6277
题意: 和上一题一样,可以理解为求最大的 h h h,其中 h h h满足权值大于 h h h的物品数量也大于$h,只不过这里题意稍微改变了一下,这里给了一个 n n n a a a,意味总共有 x x x小时,可以选择工作 x x x小时得到一篇paper(物品),其中paper的引用次数(权值)为 a ∗ x a * x ax,并且之后的发的paper可以引用以前发的paper,即之前的paper的引用次数(权值)可以+1,简单的说就是当前这个物品后面还有几个物品,那么他就可以加多少值。
题解: 没有什么好说的,我是纸上手动打表发现规律的。答案就是 ( a + n ) / 2 (a + n) / 2 (a+n)/2
代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
const int MAX = 2e5 + 10;
int main() {
	ll a, b;
	while (scanf("%lld%lld", &a, &b) != EOF) {
		printf("%lld\n", (a + b) / 2);
	}
	return 0;
}

C题 Just h-index

C题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6278
题意: 一样的求最大的 h h h,其中 h h h满足权值大于 h h h的物品数量也大于$h,只不过这里是求给定的区间 [ L , R ] [L, R] [L,R] 中最大的 h h h
题解: 这题是补的。查了一下发现原来有主席树这个东西,学了一天把这题A了。这里做法就是主席树+二分查找,二分这里要求的h(h范围是 [ 0 , m a x ( a i ) ] [0, max(a_i)] [0,max(ai)]),记 m i d = ( l + r ) / 2 mid = (l + r) / 2 mid=(l+r)/2,每次查询两个状态 T [ x − 1 ] T[x - 1] T[x1] T [ y ] T[y] T[y]之间大于 m i d mid mid的数的个数 n u m num num,若 n u m num num大于等于 m i d mid mid,说明h可以更大,所以 l = m i d + 1 l = mid + 1 l=mid+1,反之 r = m i d − 1 r = mid - 1 r=mid1
代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
#include<cstdlib>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 1e5 + 10;
int a[MAX], b[MAX], T[MAX], L[MAX << 5], R[MAX << 5], sum[MAX << 5];
int cnt = 0, N, Q, num;
inline int build(int l, int r) {
	int root = ++cnt;
	sum[root] = 0;
	if (l < r) {
		int m = (l + r) >> 1;
		L[root] = build(l, m);
		R[root] = build(m + 1, r);
	}
	return root;
}

inline int update(int pre, int l, int r, int x) {
	int root = ++cnt;
	L[root] = L[pre], R[root] = R[pre], sum[root] = sum[pre] + 1;
	if (l < r) {
		int m = (l + r) >> 1;
		if (x <= m)L[root] = update(L[pre], l, m, x);
		else R[root] = update(R[pre], m + 1, r, x);
	}
	return root;
}

inline void ask_interval(int u, int v, int l, int r, int h) {//查找大于h的数目个数
	if (h <= b[l]) {//当前数的范围是[b[l], b[r]],所以若h<=b[l]那么这个这个区间内的数都是大于h的,所以num加上区间内的数的个数
		num += sum[v] - sum[u];//sum[v] - sum[u]就是区间内数的个数
		return;
	}
	if (h > b[r])return;//与上面相反
	int m = (l + r) >> 1;
	ask_interval(L[u], L[v], l, m, h);
	ask_interval(R[u], R[v], m + 1, r, h);
}

void init() {
	cnt = 0;
	memset(T, 0, sizeof(T));
	memset(L, 0, sizeof(L));
	memset(R, 0, sizeof(R));
	memset(sum, 0, sizeof(sum));
}
int main() {
	while (~scanf("%d%d", &N, &Q)) {
		for (int i = 1; i <= N; i++) {
			scanf("%d", &a[i]);
			b[i] = a[i];
		}
		sort(b + 1, b + 1 + N);
		int n = unique(b + 1, b + 1 + N) - b - 1;
		T[0] = build(1, n);
		for (int i = 1; i <= N; i++) {
			int t = lower_bound(b + 1, b + 1 + n, a[i]) - b;
			T[i] = update(T[i - 1], 1, n, t);
		}
		while (Q--) {
			int x, y;
			scanf("%d%d", &x, &y);
			int l = 0, r = b[n], mid, ans = 0;
			while (l <= r) {
				num = 0;//记录大于mid的数的个数
				mid = (l + r) >> 1;
				ask_interval(T[x - 1], T[y], 1, n, mid);
				if (mid <= num)l = mid + 1, ans = max(ans, mid);//mid <= num说明h还可以更大
				else r = mid - 1;//h只能更小
			}
			printf("%d\n", ans);
		}
		init();
	}
	return 0;
}

F题 Sorting

F题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6281
题意: 很水,就是根据题意排个序。
题解: 这里小数的比较我有点迷,wa了一发,所以我直接将比较方法转化为 ( a p i − 1 + b p i − 1 ) ∗ ( a p i + b p i + c p i ) &lt; = ( a p i + b p i ) ∗ ( a p i − 1 + b p i − 1 + c p i − 1 ) (a_{p_i−1} + b_{p_i−1}) * (a_{p_i} + b_{p_i} + c_{p_i}) &lt;= (a_{p_i} + b_{p_i}) * (a_{p_i - 1} + b_{p_i - 1} + c_{p_i - 1}) (api1+bpi1)(api+bpi+cpi)<=(api+bpi)(api1+bpi1+cpi1),转化为整数的比较,然后就过了。但是乘积最大为 24 ∗ 1 0 18 24 * 10^{18} 241018实际上是大于unsigned long long的,所以数据么。
代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
using namespace std;
typedef unsigned long long ull;
const int MAX = 1e3 + 10;
struct node {
	int n;
	ull s, c;
}x[MAX];
bool cmp(const node &a, const node &b) {
	ull n1 = a.s * (b.s + b.c), n2 = b.s * (a.s + a.c);
	if (n1 == n2)return a.n < b.n;
	return n1 < n2;
}
int main() {
	int n;
	ull a, b, c;
	while (~scanf("%d", &n)) {
		for (int i = 1; i <= n; i++) {
			x[i].n = i;
			scanf("%lld%lld%lld", &a, &b, &x[i].c);
			x[i].s = a + b;
		}
		sort(x + 1, x + 1 + n, cmp);
		for (int i = 1; i <= n; i++) {
			if (i != 1)printf(" ");
			printf("%d", x[i].n);
		}
		printf("\n");
	}
	return 0;
}

G题 Just h-index

G题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6282
题意: 给定两个字符串 s 1 , s 2 s_1,s_2 s1,s2,其中字符串只含有 a , b , c a,b,c a,b,c三种字符,现问字符串 s 1 s_1 s1能否通过变换得到字符串 s 2 s_2 s2,其中变换规则为增加或者删除,只能操作 a b a b , a a , b b abab, aa, bb abab,aa,bb
题解: 首先,注意到字符 c c c是不能增加也不能删除的,所以当字符串 s 1 , s 2 s_1, s_2 s1,s2中字符 c c c的个数不同时,肯定不能。
然后注意到,字符串 a b ab ab 可以变换为 b a ba ba (样例就是),这也就说明了一点,在两个字符 c c c中间的 a a a b b b的次序不重要,因为他们可以互换位置,因此只需要考虑两个字符 c c c之间 a a a b b b的数量的奇偶性,只要 s 1 s_1 s1 s 2 s_2 s2中同样的两个字符 c c c之间 a a a出现次数的奇偶性相同并且 b b b出现次数的奇偶性相同就可以转换。
代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
int main() {
	char s1[10000 + 50], s2[10000 + 50];
	int sn1[10000+50][2], sn2[10000+50][2];
	while (~scanf("%s%s", s1, s2))
	{
		int num1 = 0, num2 = 0, l1 = strlen(s1), l2 = strlen(s2);
		for (int i = 0; i < l1; i++)
		{
			if (s1[i] == 'c')
				num1++;   //num1为s的c的数量
		}
		for (int i = 0; i < l2; i++)
		{
			if (s2[i] == 'c')
				num2++;    //num2为t的c的数量
		}
		if (num1 != num2)
			printf("No\n");
		else
		{
			num1++;
			s1[l1++] = 'c';
			s2[l2++] = 'c';
			s1[l1] = '\0';
			s2[l2] = '\0';
			int num = 0, a = 0, b = 0;
			for (int i = 0; i < l1; i++)
			{
				if (s1[i] == 'c')
				{
					sn1[num][0] = a % 2;
					sn1[num++][1] = b % 2;
					a = 0, b = 0;
				}
				if (s1[i] == 'a')a++;
				if (s1[i] == 'b')b++;
			}
			num = 0;
			for (int i = 0; i < l2; i++)
			{
				if (s2[i] == 'c')
				{
					sn2[num][0] = a % 2;
					sn2[num++][1] = b % 2;
					a = 0, b = 0;
				}
				if (s2[i] == 'a')a++;
				if (s2[i] == 'b')b++;
			}
			int flag = 0;
			for (int i = 0; i < num1; i++)
			{
				if (sn1[i][0] != sn2[i][0] || sn1[i][1] != sn2[i][1])
				{
					flag = 1;
					break;
				}
			}
			if (flag == 1)
				printf("No\n");
			else
				printf("Yes\n");
		}
	}
}

K题 2018

K题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6286
题意: 给定两个区间 [ L 1 , R 1 ] , [ L 2 , R 2 ] [L_1, R_1], [L_2, R_2] [L1,R1],[L2,R2],求有多少组不同的 x ∈ [ L 1 , R 1 ] , y ∈ [ L 2 , R 2 ] x∈[L_1, R_1], y∈[L_2, R_2] x[L1,R1],y[L2,R2]使得 x ∗ y x*y xy为2018的倍数。
题解: 肯定是先求2018的因数,这里发现只有1 * 2018 和 2 * 1009 这两种情况,所以只需要找各个区间内有几个 1009的倍数 、 2018的倍数和偶数。这里肯定不可能将区间扫完求个数,可以可以用等差数列的思想,比如要找2018倍数的个数,先找第一个2018出现的位置pos,找的到的话2018倍数的个数num = r1 / 2018 - pos1 / 2018 + (r1 % 2018 == 0 ? 1 : 0)。
代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
const int MAX = 2e5 + 10;
int main() {
	int l1, l2, r1, r2;
	while (scanf("%d%d%d%d", &l1, &r1, &l2, &r2) != EOF) {
		int flag1 = 0, flag2 = 0, pos1 = -1, pos2 = -1;
		for (int i = l1; i <= r1; i++)
		{
			if (i % 1009 == 0)
			{
				flag1 = 1;
				pos1 = i;
				break;
			}
		}
		for (int i = l2; i <= r2; i++)
		{
			if (i % 1009 == 0)
			{
				flag2 = 1;
				pos2 = i;
				break;
			}
		}
		if (flag1 == 0 && flag2 == 0)
			printf("0\n");
		else
		{
			int num1 = 0, num2 = 0, num3 = 0, num4 = 0;
			ll sum = 0;
			if (flag1 != 0)
			{
				num1 = r1 / 2018 - pos1 / 2018;
				if (pos1 % 2018 == 0)num1++;
				num2 = max(0, r1 / 1009 - pos1 / 1009 + 1 - num1);
			}
			if (flag2 != 0)
			{
				num3 = r2 / 2018 - pos2 / 2018;
				if (pos2 % 2018 == 0)num3++;
				num4 = max(0, r2 / 1009 - pos2 / 1009 + 1 - num3);
			}
			int num5;
			if (l1 % 2 == 0 && r1 % 2 == 0)
				num5 = (r1 - l1 + 1) / 2 + 1;
			else
				num5 = (r1 - l1 + 1) / 2;
			int num6;
			if (l2 % 2 == 0 && r2 % 2 == 0)
				num6 = (r2 - l2 + 1) / 2 + 1;
			else
				num6 = (r2 - l2 + 1) / 2;
			sum = sum + 1ll * num1*(r2 - l2 + 1) + 1ll * num3*(r1 - l1 + 1) + 1ll * num2*(num6 - num3) + 1ll * num4*(num5 - num1) - 1ll * num1*num3;
			printf("%lld\n", sum);
		}
	}
	return 0;
}

其他题目待补中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值