今天,打了一下北邮周行算协的寒假比赛,我只是随便打打,就打了一下第二赛道,据说是codeforces div3难度。大约花了差不多3小时,过了9题(一共10题),H题是一个位运算题,我是拆位考虑的,结合一下贪心思想,调了一个小时,始终不能通过,决定还是等一下那个题的题解吧,我就简单讲解一下我通过的9道题。
题目链接: http://zxoj.top:81/contest/1
A. 等差数列
思路:给你等差数列的前两项,和项数,求等差数列的和。直接使用等差数列求和公式,注意开long long。
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a, b, n;
int main(){
cin >> a >> b >> n;
ll d = b - a;
ll c = a + d*(n - 1);
ll ans = (a + c)*n/2;
cout << ans << "\n";
return 0;
}
B. 士兵选择
思路:数据量比较小,直接开 O ( n 2 ) O(n^2) O(n2)暴力就好了。
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
int n, d;
int a[1010];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> d;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
int ans = 0;
for(int i = 1; i <= n; i++){
for(int j = i+1; j <= n; j++){
if(abs(a[i] - a[j]) <= d){
if(i == j)
ans++;
else
ans += 2;
}
}
}
cout << ans << "\n";
return 0;
}
C. 递增序列
思路:一次只能加d,最少加多少次让序列严格单调递增。贪心思想,如果这个数小于等于上一个数才需要加。具体加多少次,要注意细节。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll d;
ll a[2010];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> d;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
ll ans = 0;
for(int i = 2; i <= n; i++){
if(a[i] > a[i-1]){
continue;
}
ll tmp = a[i-1] - a[i];
ll now = tmp / d + 1;
ans += now;
a[i] += d*now;
}
cout << ans << "\n";
return 0;
}
D. Easy Problem
思路:找规律。如果字符串字符数目是1,是yes。如果字符串数量大于1,并且有一个字符出现了两次及以上,是yes。否则就是no。
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int cnt[30];
string s;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> s;
if(s.length() == 1){
cout << "YES" << "\n";
return 0;
}
for(int i = 0; i < s.length(); i++){
cnt[s[i]-'a']++;
}
int ok = 0;
for(int i = 0; i < 26; i++){
if(cnt[i] > 1){
ok = 1;
break;
}
}
if(ok){
cout << "YES" << "\n";
}
else{
cout << "NO" << "\n";
}
return 0;
}
E. 数列
思路:直接模拟就行。注意好上一个01分界点,10分界点在哪里。
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
int vis[100010];
int change[100010];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> m;
cin >> n;
for(int i = 1; i <= m; i++){
cin >> vis[i] >> change[i];
}
int last0 = -1, last1 = -1;
int max0 = 0, max1 = 0;
if(change[1]){
last1 = vis[1];
}
else{
last0 = vis[1];
}
for(int i = 2; i <= m; i++){
if(change[i] && change[i-1]){
}
else if(change[i] && !change[i-1]){
if(last0 != -1){
max0 = max(max0, vis[i] - last0);
}
last1 = vis[i];
last0 = -1;
}
else if(!change[i] && change[i-1]){
if(last1 != -1){
max1 = max(max1, vis[i] - last1);
}
last0 = vis[i];
last1 = -1;
}
else{
}
}
if(change[m]){
max1 = max(max1 , n - last1 + 1);
}
else{
max0 = max(max0, n - last0 + 1);
}
cout << max1 << " " << max0 << "\n";
return 0;
}
F. DNA
这个题n是100000。直接 O ( n 2 ) O(n^2) O(n2)暴力过不了,考虑优化成 O ( n l o g n ) O(nlogn) O(nlogn)。我们发现,一个字符串的最多20个字符,我们可以从这里入手。我们要预处理出题里所有的字符串,以及一个字符串出现了几次,需要对字符串进行重新编号。我们求出答案的时候,遍历到一个字符串,只需要看它的配对字符串在题目中出现了几次就好,我们可以将以上操作用哈希实现,比较高效。C++用map类就行。
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
string s[100010];
int n;
map<string, int> cnt;
map<string, int> id;
string t[100010];
int vis[100010];
int tot = 0;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++){
cin >> s[i];
if(id.find(s[i]) == id.end()){
id[s[i]] = ++tot;
cnt[s[i]]++;
t[tot] = s[i];
}
else{
cnt[s[i]]++;
}
}
ll ans = 0;
for(int i = 1; i <= tot; i++){
if(!vis[i]){
//cout << i << "\n";
string tmp = t[i];
string mat = "";
for(int i = 0; i < tmp.length(); i++){
if(tmp[i] == 'A'){
mat += 'T';
}
else if(tmp[i] == 'T'){
mat += 'A';
}
else if(tmp[i] == 'C'){
mat += 'G';
}
else{
mat += 'C';
}
}
//cout << tmp << " " << mat << "\n";
if(id.find(mat) == id.end()){
}
else{
ans += 1ll*min(cnt[mat], cnt[tmp]);
vis[id[mat]] = 1;
}
vis[i] = 1;
}
}
cout << ans << "\n";
return 0;
}
G. 三棱锥
思路:dp。我们设一个数组dp[n][m]表示第n步之后到m的方案数。假设三棱锥的四个点是0,1,2,3。开始时候,在三棱锥的0点处, 所以dp[0][0] = 1,我们规划出dp[n][0]就是答案。
dp[i][0] = ((dp[i-1][1] + dp[i-1][2])%mod + dp[i-1][3])%mod;
dp[i][1] = ((dp[i-1][0] + dp[i-1][2])%mod + dp[i-1][3])%mod;
dp[i][2] = ((dp[i-1][1] + dp[i-1][0])%mod + dp[i-1][3])%mod;
dp[i][3] = ((dp[i-1][1] + dp[i-1][2])%mod + dp[i-1][0])%mod;
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
int dp[10000001][4];
int n;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
dp[0][0] = 1;
for(int i = 1; i <= n; i++){
dp[i][0] = ((dp[i-1][1] + dp[i-1][2])%mod + dp[i-1][3])%mod;
dp[i][1] = ((dp[i-1][0] + dp[i-1][2])%mod + dp[i-1][3])%mod;
dp[i][2] = ((dp[i-1][1] + dp[i-1][0])%mod + dp[i-1][3])%mod;
dp[i][3] = ((dp[i-1][1] + dp[i-1][2])%mod + dp[i-1][0])%mod;
}
cout << dp[n][0]%mod << "\n";
return 0;
}
I. Hard Problem I
思路:贪心,如果s[i] = s[i-1],我们让s[i]变成’#’,ans++,就可以得出答案。因为,一个字符能否构成两个连续相同字符,之和s[i-1],s[i+1],有关,如果s[i] = s[i-1], 无论s[i+1]是什么,我们都可以保证s[i]进行修改之后,不等于s[i+1]。
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
string s;
int cnt;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> s;
for(int i = 1; i < s.length(); i++){
if(s[i] == s[i-1]){
s[i] = '#';
cnt++;
}
}
cout << cnt << "\n";
}
J. 消息传播
思路:这题是一个思维题。i知道消息,下一天所有
g
c
d
(
i
+
1
,
j
+
1
)
=
1
gcd(i+1, j+1) = 1
gcd(i+1,j+1)=1的都会知道消息。我们把原序列中的i都+1,k也+1。题目就是
2
,
3
,
4
,
,
,
,
n
+
1
2,3,4,,,,n+1
2,3,4,,,,n+1人,第0天k+1知道消息,问第几天所有人都知道消息。
我们肯定要对k+1这个数进行因数分解。我们可以知道a,和(a+1或者 a-1)的因数除了1以外都是不同的。因为不存在i, i+1同时被k整除且k != 1。所以第0天k+1,知道消息,第一天k+2,k就一定知道消息。而第一天不能接受消息的,第二天都可以通过k, k+2知道消息。所以本题的最终答案要么是1,要么是2。
所以,我们只要在最多
O
(
n
)
O(\sqrt{n})
O(n),找到k+1这个数除了1以外的最小因子m就行。如果2*m > n,就是这个序列中所有数和k+1的最大公因数是1,就需要1天,否则需要2天。
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, k;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
n++;
k++;
int ok = 0;
ll now = 1;
for(ll i = 2; i * i <= k; i++){
if(k % i == 0){
ok = 1;
now = i;
break;
}
}
if(!ok){
now = k;
}
if(now*2 > n){
cout << "1" << "\n";
}
else{
cout << "2" << "\n";
}
return 0;
}