2019级吉林大学XCPC集训队选拔赛 - 第一轮 补题记录

Problem C

在这里插入图片描述
在这里插入图片描述
理解清楚7的出现次数是指什么,对于每一位的贡献有多少

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
const int maxn = 100000 + 5;
char s[maxn];
ll p[maxn], ppgod[maxn], postfix[maxn];
int main(void) {//1000对7的贡献=个位7贡献10*10,十位7贡献10*10,百位7贡献10*10
	p[0] = 0;	//10000对7的贡献=个/十/百/千位的7都贡献10*10*10
	ppgod[0] = 1;
	for (int i = 1; i <= 100000; i++) {
		p[i] = (ppgod[i - 1] * i) % mod;//i*10^(i-1)
		ppgod[i] = (ppgod[i - 1] * 10) % mod;//i表示后缀0的个数
		//ppgod[i]表示10的i次方
	}
	int t; scanf("%d", &t);
	while (t--) {
		scanf("%s", s);
		int len = strlen(s);
		postfix[len] = 0;//求从个位开始直到第len-i位的数值
		for (int i = len - 1; i >= 0; i--) {
			int x = s[i] - '0';
			//本位是第len-i位,要乘以10^(len-i-1)
			postfix[i] = (postfix[i + 1] + x * ppgod[len - i - 1]) % mod;
		}
		ll ans = 0;
		for (int i = 0; i < len; i++) {
			int x = s[i] - '0';//本位是第len-i位
			ans = (ans + x * p[len - 1 - i]) % mod;
			if (x > 7)
				ans = (ans + ppgod[len - i - 1]) % mod;//本位的7贡献10^(len-i-1)
			else if (x == 7) 
				ans = (ans + postfix[i + 1] + 1) % mod;//本位的7贡献+1(0...0) 以及加上后面的贡献
		}
		printf("%lld\n", ans);
	}
	return 0;
}


Problem D

在这里插入图片描述
在这里插入图片描述
求最大被5整除序列和,查了一下求最长序列,知道了用mod的方法

代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
int n;
ll a[maxn], dp[5], tmp[5];
int main(void) {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		for (int j = 0; j < 5; j++)
			if (j == 0 || tmp[j] != 0) {
				int xb = (j + a[i]) % 5;
				dp[xb] = max(dp[xb], tmp[j] + a[i]);
			}
		int xb = a[i] % 5;
		tmp[xb] = max(tmp[xb], a[i]);
		for (int j = 0; j < 5; j++) {
			tmp[j] = max(tmp[j], dp[j]);
		}
	}
	printf("%lld\n", tmp[0]);
	return 0;
}


Problem E

在这里插入图片描述
在这里插入图片描述
问要多少步可以关掉全部的灯,每次必须先关入度为0的灯,拓扑一下就可以了。

#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 4e4 + 5;
int n, m, in[maxn];
bool on[maxn], vis[maxn];
vector<int>nxt[maxn];
void switchIt(int id) {
	on[id] = !on[id];
	for (int i = 0; i < nxt[id].size(); i++) {
		int v = nxt[id][i];
		if (vis[v])continue;
		vis[v] = true;
		switchIt(v);
	}
}
int main(void) {
	on[1] = true;
	scanf("%d %d", &n, &m);
	while (m--) {
		int u, v;
		scanf("%d %d", &u, &v);
		nxt[u].push_back(v);
		in[v]++;
	}
	queue<int>Q;
	for (int i = 1; i <= n; i++)
		if (!in[i])Q.push(i);
	vector<int>range;
	while (!Q.empty()) {
		int u = Q.front(); Q.pop();
		range.push_back(u);
		for (int i = 0; i < nxt[u].size(); i++) {
			int v = nxt[u][i];
			in[v]--;
			if (!in[v])Q.push(v);
		}
	}
	int cnt = 0;
	for (int i = 0; i < range.size(); i++) {
		if (!on[range[i]])continue;
		memset(vis, 0, sizeof(vis));
		switchIt(range[i]);
		cnt++;
	}
	printf("%d\n", cnt);
	return 0;
}


Problem F

在这里插入图片描述
在这里插入图片描述
以前没看懂"|"是整除的意思,后知后觉这是水题

#include<cstdio>
using namespace std;
typedef long long ll;
const int limit = 1e6;
const int maxn = 1e6 + 5;
ll sum[maxn];
int Q, L, R;
int main(void) {
	for (int i = 1; i <= limit; i++) {
		for (int j = i; j <= maxn; j += i)
			sum[j] += i;
	}
	for (int i = 2; i <= limit; i++)
		sum[i] += sum[i - 1];
	scanf("%d", &Q);
	while (Q--) {
		scanf("%d %d", &L, &R);
		printf("%lld\n", sum[R] - sum[L - 1]);
	}
	return 0;
}


Problem G

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

题解
期末考试:
将所有a换成二进制,统计每一位上有几个数为1,这样问题就转换成有若干个物品,
每个物品花费2的若干次方,得到的价值为2的若干次方乘上一个数,每种物品有两个形态,
一个形态表示k这一位为0,则价值乘上的数为a中这一位为1的数的个数,
另一个形态表示k这一位为1,价值乘上的数为a中这一位为0的数的个数。
一开始我们先贪心求出一个k使得sigma(a^k)最小,这个贪心是显然的,
之后我们从高位往低位贪心求k,
如果这一位k已经为1,则不用管直接继续下一位,
如果这一位k为0,那么贪心取,
如果这一位k为1的价值加上已有价值小于等于m的话,那这一位一定为1,
否则k这一位一定为0(为1就不符合题意了)最后输出得到的k就是答案
注意一下数据,这个数据很大,看似需要高精度,实际上只需要一个特判就可以判掉无解
时间复杂度o(40(n+q))

#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
int n, a, Q, zeroCnt[51], oneCnt[51];
ll m, pow2[51];
int main(void) {
	scanf("%d", &n);
	for (int i = 31; i <= 50; i++)zeroCnt[i] = n;
	for (int i = 0; i < n; i++) {
		scanf("%d", &a);
		for (int j = 0; j <= 30; j++) {
			if (a & 1)
				oneCnt[j]++;
			else 
				zeroCnt[j]++;
			a >>= 1;
		}
	}
	pow2[0] = 1;
	for (int i = 1; i <= 50; i++)
		pow2[i] = pow2[i - 1] << 1;//2^i
	scanf("%d", &Q);
	while (Q--) {
		scanf("%lld", &m);
		ll k = 0, sum = 0;
		for (int i = 0; i <= 50; i++) {//先贪心求k使得和最小
			if (oneCnt[i] > zeroCnt[i]) {//1多,本位补1
				k += pow2[i];
				sum += zeroCnt[i] * pow2[i];
			}
			else
				sum += oneCnt[i] * pow2[i];
		}
		if (sum > m) {
			printf("-1\n"); continue;
		}
		for (int i = 50; i >= 0; i--) {
			if (k & pow2[i])continue;
			ll tmp = pow2[i] * (zeroCnt[i] - oneCnt[i]);
			if (tmp < 0)continue;
			if (sum + tmp > m)continue;
			sum += tmp;
			k += pow2[i];
		}
		printf("%lld\n", k);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JILIN.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值