2020牛客国庆集训派对day1 签到题题解
前情摘要:退役选手,一个人不想读英文题,只做签到题0.0
A. ABB
Solution
题意:可以在后面添加字符,使得原串变成回文串,问需要添加的最少字符数。
思路:Manacher里的p[i]数组指以某个点为中心的回文半径,利用这个数组就可以做这道题,模板是网上随手复制的。
时间复杂度
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
char s[11000002];
char s_new[21000002];//存添加字符后的字符串
int p[21000002];
int n;
int Init() {//形成新的字符串
int len=strlen(s);//len是输入字符串的长度
s_new[0]='$';//处理边界,防止越界
s_new[1]='#';
int j=2;
for(int i=0;i<len;i++) {
s_new[j++]=s[i];
s_new[j++]='#';
}
s_new[j]='\0';//处理边界,防止越界(容易忘记)
return j;// 返回s_new的长度
}
int Manacher() {//返回最长回文串
int len=Init();//取得新字符串的长度, 完成向s_new的转换
int max_len=-1;//最长回文长度
int id;
int mx=0;
for(int i=1;i<=len;i++) {
if(i<mx)
p[i]=min(p[2*id-i],mx-i);//上面图片就是这里的讲解
else p[i]=1;
while(s_new[i-p[i]]==s_new[i+p[i]])//不需边界判断,因为左有'$',右有'\0'标记;
p[i]++;//mx对此回文中点的贡献已经结束,现在是正常寻找扩大半径
if(mx<i+p[i]) {//每走移动一个回文中点,都要和mx比较,使mx是最大,提高p[i]=min(p[2*id-i],mx-i)效率
id=i;//更新id
mx=i+p[i];//更新mx
}
max_len=max(max_len,p[i]-1);
}
int ans = 1e9;
// for(int i = 1; i <= len; i++) {
// cout << i << ' ' << p[i] << "\n";
// }
for(int i = len; i >= 1; i--) {
if(p[i] + i >= len) {
ans = min(ans, n - p[i] + 1);
}
}
return ans;
}
int main() {
cin >> n;
cin >> s;
cout << Manacher() << "\n";
return 0;
}
C. Bob in Wonderland
Solution
题意:给一个图,对于每条边可以断开重连,想要得到一条链
思路:最终一条链那么最后他们的度为1(起始点) 2 2 .... 2 1(结束点),直接对度大于2的点进行处理,设每个点的度为 , 花费为
,可以理解为将他们断开连接到度为1的点。
时间复杂度
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
vector<int> G[N];
int main() {
int n; cin >> n;
for(int i = 1; i <= n - 1; i++) {
int u, v; cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
ll ans = 0;
for(int i = 1; i <= n; i++) {
if(G[i].size() > 2)
ans += (G[i].size() - 2);
}
cout << ans << "\n";
return 0;
} E. Zeldain Garden
题意:找[l, r]区间内每个数字的因子数
思路:前缀和的思想求
要求区间的因子数可以想到 [1, r]里
1出现 次
2出现 次
......
最终答案就是经典问题整除分块
时间复杂度
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
ll solve(ll n) {
ll res = 0;
for(ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
res += (r - l + 1) * (n / l);
}
return res;
}
int main() {
ll l, r; cin >> l >> r;
cout << solve(r) - solve(l - 1) << "\n";
return 0;
}
H. PPonk Warshall
Solution
题意:字符串A最少能经过多少次两两交换变成字符串B
思路:分三种情况:
- A B 这种情况,能够一次交换使得两两对应
B A - A B C 这种情况,能够两次交换使得两两对应
B C A - A B C D 这种情况,能够三次交换使得两两对应
B C D A
于是只需要一个二维数组存储字符间的关系,随后三种情况从第一种开始枚举即可。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include<map>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
map<char, int> mp;
int maze[10][10];
void init() {
mp['A'] = 1, mp['C'] = 2, mp['G'] = 3, mp['T'] = 4;
}
int solve(int x, int y, int z) {
int ans = 0;
int l = min(maze[x][y], min(maze[y][z], maze[z][x]));
int r = min(maze[x][z], min(maze[z][y], maze[y][x]));
ans += 2 * (l + r);
maze[x][y] -= l, maze[y][z] -= l, maze[z][x] -= l;
maze[x][z] -= r, maze[z][y] -= r, maze[y][x] -= r;
return ans;
}
int main() {
init();
string s, t; cin >> s >> t;
for(int i = 0; i < s.size(); i++) {
if(s[i] == t[i]) continue;
maze[mp[s[i]]][mp[t[i]]]++;
}
int ans = 0;
for(int i = 1; i <= 4; i++) {
for(int j = i + 1; j <= 4; j++) {
if(maze[i][j] >= maze[j][i]) {
maze[i][j] -= maze[j][i];
ans += maze[j][i];
maze[j][i] = 0;
} else {
maze[j][i] -= maze[i][j];
ans += maze[i][j];
maze[i][j] = 0;
}
}
}
for(int i = 1; i <= 4; i++) {
for(int j = i + 1; j <= 4; j++) {
for(int k = j + 1; k <= 4; k++) {
ans += solve(i, j, k);
}
}
}
for(int i = 1; i <= 4; i++) {
ans += 3 * maze[1][i];
}
cout << ans << "\n";
return 0;
}
F. Light Emitting Hindenburg
Solution
题意:n个数字里取k个,使得按位与最大
思路:二进制位上从大到小考虑,用一个数组标记作为备选的数字,对于高位上不为1的数字都可以排除。于是从高位往低位贪心取即可。
时间复杂度
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include<map>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
ll a[N];
int vis[70];
int need[N];
int main() {
int n, k; cin >> n >> k;
for(int i = 1; i <= n; i++) cin >> a[i], need[i] = 1;
ll ans = 0;
for(int i = 63; i >= 0; i--) {
for(int j = 1; j <= n; j++) {
if(need[j] && ((a[j] >> i) & 1)) {
vis[i]++;
}
}
//cout << i << ' ' << vis[i] << "\n";
if(vis[i] >= k) {
ans += (1LL << i);
for(int j = 1; j <= n; j++) {
if(!need[j]) continue;
if((!((a[j] >> i) & 1)) && need[j]) {
need[j] = 0;
}
}
}
}
cout << ans << "\n";
return 0;
}
字节跳动公司福利 1297人发布