KDY十一月月赛入门赛第一场补题报告

AC情况

第一题第二题第三题第四题
AC 100TLE 0 (AC 100)- (AC 100)-

赛中概况

第一题很快AC,后面第二题超时,剩下的题目没有看。

解题报告

异或(xor)

情况:

AC

题意:

给定 n n n 个二进制数,求 a 1 ⊕ a 2 ⊕ . . . a n a_1 \oplus a_2 \oplus ... a_n a1a2...an 的结果。

题解:

模拟即可。

int get(char c) {
    return c - '0';
}

for (int i = 1; i <= n; i++) {
	cin >> s;
	if (i == 1) ans = s;
	else {
		for (int i = 0; i < m; i++)
			ans[i] = get(ans[i]) ^ get(s[i]) + '0';
	}
}

数学游戏(math)

情况:

TLE (AC)

题意:

f ( i ) f(i) f(i) i i i 各位数字相加的和。

求出 x x x 的所有约数 d d d.

g ( x ) = ∑ ( x d i × f ( d i ) ) g(x) = \sum (x^{d_i} \times f(d_i)) g(x)=(xdi×f(di))

∑ i = 1 n g ( i ) \sum^n_{i=1}g(i) i=1ng(i)(答案对 1 0 9 + 7 10^9 + 7 109+7 取余).

赛事做法:

模拟求解。

题解:

先把从 1 1 1 n n n 所有的 f ( i ) f(i) f(i) 都求出来。

运用类似于埃氏筛的方法:

遍历 i ( 1 ≤ i ≤ n ) i (1 \le i \le n) i(1in),再遍历 j ( 1 ≤ j ≤ ⌊ n i ⌋ ) j (1 \le j \le \lfloor\frac{n}{i}\rfloor) j(1jin⌋) ,找出 i i i [ 1 , n ] [1,n] [1,n] 范围内所有的倍数,那么 i i i 一定是 g ( i j ) g(ij) g(ij) 的一个约数。 g ( i j ) g(ij) g(ij) 累计 f ( i ) × ( i j ) i f(i) \times (ij)^i f(i)×(ij)i.

取余的性质(取余的性质不适用于除法):

( a ± b ) % c = ( a % c ± b % c ) % c ,    a × b % c = [ ( a % c ) × ( b % c ) ] % c (a \pm b) \% c = (a \% c \pm b \% c) \% c, \ \ a \times b \% c =[(a \% c) \times (b \% c)] \% c (a±b)%c=(a%c±b%c)%c,  a×b%c=[(a%c)×(b%c)]%c

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const long long int mod = 1e9 + 7;
const long long int N = 1000010;
ll n, p[N], g[N];
ll f(ll n) {
    ll res = 0;
    while (n) {
        res += n % 10;
        n /= 10;
    }
    return res % mod;
}
ll qpow(ll a, ll b, ll mod) {  
    a %= mod;  
    ll res = 1;  
    while (b > 0) {  
        if (b & 1) res = (res * a) % mod;  
        a = (a * a) % mod;  
        b >>= 1;  
    }  
    return res;  
}
int main() {
    cin >> n;
    for (ll i = 1; i <= n; i++) 
    	p[i] = f(i);
    ll res = 0;
    for (ll i = 1; i <= n; i++)
        for (ll j = 1; i * j <= n; j++)
        	g[i * j] = (g[i * j] + p[i] * qpow(i * j, i, mod); % mod) % mod;
    for (ll i = 1; i <= n; i++) 
    	res = (res + g[i]) % mod;
    cout << res;
    return 0;
}

交换(swap)

情况:
− - (AC)
题意:

有一个长度为 n n n 的序列 a 1 , a 2 , . . . a n a_1,a_2,...a_n a1,a2,...an ,有 k k k 次操作机会,每次操作可选择任何一个数字(可以重复选择同一个数字)的任意两位进行交换(但不能出现交换后有前导 0 0 0 的情况),问进行 k k k 次操作后,序列内最小的 a i a_i ai 最大是多少。

题解:

二分查找临界值,保证所有数一共进行 k k k 次操作后都可以达到临界值及以上。

change()函数:对数字 n n n 进行一次操作。策略:从最高位到最低位寻找第一个可以被替换的位(可以被替换的位是指,这一位的后面还有比它更大的数字),把它改成后面最大的数字,然后替换掉后面的那个大数字的位(有相同数字的位替换位较低的)。代码如下

int change(int n) {
	priority_queue<int> q;
	queue<int> Q;
	int m = n, a[15], cnt = 0;
	while (m) {
		q.push(m % 10);
		a[++cnt] = m % 10;
		m /= 10;
	}
	int res = 0, flag = -1, ch;
	for (int i = cnt; i >= 1; i--) {
		if (a[i] == q.top()) q.pop();
		if (a[i] < q.top() && flag == -1) {
			ch = a[i];
			a[i] = q.top();
			flag = q.top();
		}
		else if (flag != -1 && a[i] == flag && a[i - 1] != a[i]) {
			a[i] = ch;
			break;
		}
	}
	for (int i = cnt; i >= 1; i--) 
		res = res * 10 + a[i];
	return res;
} 

check()函数:判断临界值 m i d mid mid 是否满足情况,即所有的数一共进行 k k k 次操作。策略:如果这个数小于临界值并且还有操作次数,那么一直对这个数进行操作,直到操作次数没有,或者已经在临界值及以上,或者已经把这个数操作到最大无法继续替换了(也就是每位上的数字成降序排列),不再进行操作。一旦遇到一个已经无法继续进行操作而且仍然小于临界值的数字,视为该临界值过高,无法满足情况。如果所有数字经合法的操作之后都能达到临界值,则视为该临界值为一个符合要求的解。代码如下

bool check(int mid) {
	int had = k;
	for (int i = 1; i <= n; i++) b[i] = a[i];
	for (int i = 1; i <= n; i++) {
		while (b[i] < mid && b[i] != change(b[i]) && had > 0) {
			b[i] = change(b[i]);
			had--;
		}
		if (b[i] < mid) return false;
	}
	return true;
}

main()主函数:二分查找最大的合法临界值。临界值的上限就是能够输入的最大值 1 0 9 10^9 109,最小值是能够输入的最小值 1 1 1

int main() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
    	cin >> a[i];
    int l = 1, r = 1e9 + 10, mid, ans = 0;
    while (l <= r) {
    	mid = (l + r) / 2;
    	if (check(mid)) {
    		ans = max(ans, mid);
    		l = mid + 1;
		} else {
			r = mid - 1;
		}
	}
	cout << ans;
	return 0;
}

数字串(string)

情况:

− -

题意:

现有一个长度为 n n n 的不含有 0 0 0 的数字字符串,可以进行以下操作:

  • 选择若干个不相交的连续子段并进行翻转,翻转一个长度为 L L L 的子段代价为 L L L
  • 在字符串中插入不超过 n − 1 n-1 n1 个乘号,代价为0

现在最多可使用 v v v 的代价,问进行多次操作之后,字符串计算结果对 m m m 取余后最大是多少。

总结

多想一想以前学过的算法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值