题意:很简单的题,在数轴上给定两个点,两个人相向而行,每个人走第x格的体力花费是x。问你两个人怎么走,直到见面时体力花费加和最小。
思路:大力感知一下就好了。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int a, b;
scanf("%d%d", &a, &b);
int delta = max(a, b) - min(a, b);
int n = delta / 2;
int ans = (1 + n) * n + (delta & 1 ? n + 1 : 0);
printf("%d\n", ans);
return 0;
}
题意:1-n,进行锦标赛,相邻的俩俩胜者不断决斗直到最后,问a和b两个队伍能否在总决赛相会。
思路:如果把整个过程看成一颗二叉树。显然就是一个不断找父节点的问题,对于二叉树我们很熟悉,子节点编号/2就是父节点的编号,不断向上即可,看看他们的共同父节点(或者说是LCA)是否为根即可。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, a, b;
scanf("%d%d%d", &n, &a, &b);
int ans = 0;
while(a != b)
{
a = (a + 1) / 2, b = (b + 1) / 2;
ans++;
}
if((1 << ans) == n) puts("Final!");
else printf("%d\n", ans);
return 0;
}
题意:给出一个序列,其中所有元素的最大值和最小值的差保证不超过2,问找到一个序列(最大值不超过原序列的最大值,最小值不小于原序列的最小值)使得这两个序列的平均值相同且相同的元素最少,问最少的相同元素有几个,并打印出该序列。
思路:显然改变这个序列,无非是把2和0两个元素改变成1和1,或者把两个1改变成2和0。按照最大最小值的差值分类讨论,如果差值为0或者1,显然最少的相同元素是n个,也就是说只能找到原序列满足条件。如果差值为2,那么就按照之前说的改变方法,取一个最少相同的即可。
#include <bits/stdc++.h>
using namespace std;
int a[100000 + 5];
int main()
{
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
sort(a, a + n);
int delta = *max_element(a, a + n) - *min_element(a, a + n);
if(delta == 2)
{
int base = *min_element(a, a + n);
int one = 0, two = 0, zero = 0;
for(int i = 0; i < n; i++)
{
if(a[i] == base) zero++;
else if(a[i] == base + 1) one++;
else two++;
}
int t1 = 2 * min(zero, two), t2 = one / 2 * 2;
if(t1 >= t2)
{
printf("%d\n", n - t1);
vector<int>ans;
for(int i = 0; i < t1 / 2; i++) ans.push_back(base + 1), ans.push_back(base + 1);
for(int i = 0; i < zero - t1 / 2; i++) ans.push_back(base + 0);
for(int i = 0; i < two - t1 / 2; i++) ans.push_back(base + 2);
for(int i = 0; i < one; i++) ans.push_back(base + 1);
for(int i = 0; i < n; i++) printf("%d%c", ans[i], i + 1 == n ? '\n' : ' ');
}
else
{
printf("%d\n", n - one / 2 * 2);
vector<int>ans;
for(int i = 0; i < t2 / 2; i++) ans.push_back(base + 0), ans.push_back(base + 2);
for(int i = 0; i < zero; i++) ans.push_back(base + 0);
for(int i = 0; i < two; i++) ans.push_back(base + 2);
for(int i = 0; i < one - t2; i++) ans.push_back(base + 1);
// cout<< t2 <<" " << zero <C< " " <<two <<" " << one - t2 << endl;
for(int i = 0; i < n; i++) printf("%d%c", ans[i], i + 1 == n ? '\n' : ' ');
}
}
else
{
printf("%d\n", n);
for(int i = 0; i < n; i++) printf("%d%c", a[i], i + 1 == n ? '\n' : ' ');
}
return 0;
}
题意:给出一棵树,然后告诉你每个节点初始有一个苹果,每一个单位时间,你会从根节点取走所有的苹果,每一个单位时间,苹果向其父节点挪动一次。保证父节点的苹果先于儿子节点的苹果挪动,所以不会有冲突。但是同一个单位时间,一个节点,每两个苹果就会发生神奇的现象,两个苹果都消失,也就是说只能保留n&1个苹果。问你一共能取走几个苹果。
思路:很容易发现就是一个&1的方式,每一步&1的过程,我们是不是可以暂时保留,最后才&1的结果是不是一样的。所以这道题就变成了,dfs以后,每一层的节点数量&1加和即可。
#include <bits/stdc++.h>
using namespace std;
vector<int>G[100000 + 5];
vector<int>level;
void dfs(int u, int fa, int deep)
{
level[deep]++;
for(auto o : G[u]) dfs(o, u, deep + 1);
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 2, x; i <= n; i++) scanf("%d", &x), G[x].push_back(i);
level.resize(n);
dfs(1, -1, 0);
int ans = 0;
for(auto o : level) ans += (o & 1);
printf("%d\n", ans);
return 0;
}
题意:给你一个小写字母字符串,A会把这个序列截断把后半部分补到前半部分之前,形成新的序列,然后告诉你这个新的序列的第一个字母是什么,并且允许你询问一次,这个新序列中任意一个位置的字母是什么,并告诉你答案,问你能否唯一的知道原序列被截断的位置,如果可以,则算你赢,问你赢的最大概率是多少。
思路:我们可以预处理出dp[i][j][k]:字母i和字母j距离为k一共在原序列中出现了几次。统计答案的时候,只需要对于每一个字母i,枚举距离为k的时候,看有几个字母j满足dp[i][j][k]=1,(这个过程等价于,告知你的第一个位置的字母是i,假设你唯一的知道这个原序列被截断的位置为k,那么A告诉你的字母为j的时候是否满足题意),显然对于每一个字母i,找到一个满足dp[i][j][k]的字母j最多的 ,将其累加起来即可。(这个过程等价于,告知你的第一个位置的字母是i,你将选择询问距离为 的那个字母j,这个举动将使你有最大的胜率)
#include <bits/stdc++.h>
using namespace std;
int cnt[26][26][5000 + 5];
int vis[26];
int main()
{
string str;
cin >> str;
for(int i = 0; i < str.size(); i++)
{
vis[str[i] - 'a'] = 1;
for(int dist = 1; dist < str.size(); dist++)
{
cnt[str[i] - 'a'][str[(i + dist) % str.size()] - 'a'][dist]++;
}
}
// puts("?");
double ans = 0;
for(int i = 0; i < 26; i++)
{
int mx = 0;
for(int dist = 1; dist < str.size(); dist++)
{
int temp = 0;
for(int j = 0; j < 26; j++)
{
if(cnt[i][j][dist] == 1) temp++;
}
mx = max(mx, temp);
}
ans += 1.0 * mx / str.size();
}
printf("%.8f\n", ans);
return 0;