2021牛客寒假算法基础集训营1 补题 ABCEFIJ
https://ac.nowcoder.com/acm/contest/9981#question
A. 串
考虑先搞出来一个串然后逐渐添加。设dp[i]为长度为i的含有us子串的串的个数,则当dp[i - 1]已经求出来的情况下,第i个位置:
-
前面已经有us,第i个位置随便添加
dp[i] += dp[i - 1] * 26;
-
前面没有us,但出现过出现了u,当前位置必须出现s
dp[i] += 26^(i - 1) - 25 ^ (i - 1) - dp[i - 1];即总数减去之前没有u的(i - 1位每一位都只有25种情况)减去之前有us的(dp[i - 1])
计数:dp / 排列组合
#include <iostream>
#define mod 1000000007
#define ll long long
using namespace std;
ll n, dp[1000005] = { 0 };//dp[i]为长度为i的包含us的串的数量
ll fpow(ll a, ll b)
{
ll ans = 1;
for (; b; b >>= 1)
{
if(b & 1)
ans = ans * a % mod;
a = a * a % mod;
}
return ans;
}
int main()
{
cin >> n;
dp[0] = dp[1] = 0;
dp[2] = 1;
for (int i = 3; i <= n; i++)
{
dp[i] = dp[i - 1] * 26 % mod;
dp[i] = (dp[i] + fpow(26, i - 1) + mod - fpow(25, i - 1) + mod - dp[i - 1]) % mod;
}
ll ans = 0;
for (int i = 1; i <= n; i++)
{
ans = (ans + dp[i]) % mod;//统计答案
}
cout << ans;
return 0;
}
B. 括号
因为形如(((...)))这样的括号,如果有n层,那么就有\(n^2\)对匹配的括号。那么直接对k开方然后向下取整得到a,极限情况下得到的大约是sqrt(1e9)为31622,乘2后也不会超过100000,至于剩下的\(k - a^2\)对括号,可以对上面的(((...)))从左往右数\(k - a^2\)个左括号,然后插入一个右括号即可。
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
int main()
{
vector <char> v;
int k;
cin >> k;
if(!k)
{
cout << '(';
return 0;
}
int a = (int)sqrt(k);
int res = k - a * a;
for(int i = 1; i <= a; i++) v.push_back('(');
for(int i = 1; i <= a; i++) v.push_back(')');
vector<char>::iterator it;
int cnt = 0;
for(it = v.begin(); it != v.end(); it++)
{
if((*it) == '(') cnt++;
if(cnt == res || cnt == a)
{
v.insert(it + 1, ')');
res -= cnt;
break;
}
}
if(res)
{
cnt = 0;
for (it = v.begin(); it != v.end(); it++)
{
if((*it) == '(') cnt++;
if(cnt == res)
{
v.insert(it + 1, ')');
break;
}
}
}
for (int i = 0; i < v.size(); i++)
cout << v[i];
return 0;
}
C. 红和蓝
从叶子结点开始分析。首先注意到一个点肯定不能有两个以上的叶子结点,因为设第一个叶子结点和父亲节点同色(满足染色要求),剩下的节点肯定都不能再与父亲节点同色,然而这样的话就无法满足它周围有与它同色的点的要求了。因此排除掉不可能的情况后,选择把所有的叶子结点与其父亲节点涂相同的颜色,同时与其他相邻的点异色。这样这些点确定后就可以去掉了,去掉之后得到的还是一棵树,同样满足这个要求,因此可以dfs,在回溯的时候:
- 这个点是叶子结点:标记其与父亲节点同色
- 这个点不是叶子结点同时没有被其儿子结点标记:标记其与其父亲节点同色。
如果一个点没有被涂色但其父亲节点被涂色:无解
如果没有儿子结点能把整棵树的根结点涂色(即除了根结点外都配对):无解
#include <iostream>
#define N 100005
using namespace std;
int n, head[N], ver[2 * N], Next[2 * N], f[N], color[N], tot = 0, cnt = 0;
bool flag = 1;
void add(int x, int y)
{
ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}
void dfs1(int x, int pre){
int son = 0;
for (int i = head[x]; i; i = Next[i])
{
int y = ver[i];
if(y == pre)
continue;
son++;
dfs1(y, x);
}
if(!son || f[x] == 0)
{
if(f[pre])
{
flag = 0;
return;
}
f[x] = f[pre] = ++cnt;
}
}
void dfs2(int x, int pre)
{
for (int i = head[x]; i; i = Next[i])
{
int y = ver[i];
if(y == pre)
continue;
if(f[y] == f[x])
color[y] = color[x];
else
color[y] = color[x] ^ 1;
dfs2(y, x);
}
}
int main(){
cin >> n;
for (int i = 1; i <= n - 1; i++){
int x, y;
cin >> x >> y;
add(x, y);
add(y, x);
}
dfs1(1, 0);//选取1为树根进行预处理
if(f[0] != 0 || !flag)
{
cout << -1;
return 0;
}
color[1] = 1;
dfs2(1, 0);
for (int i = 1; i <= n; i++)
{
if(color[i])
cout << "R";
else
cout << "B";
}
return 0;
}
E. 三棱锥之刻
高中数学几何题QAQ
#include <iostream>
#include <cmath>
using namespace std;
double a, r;
const double PI = acos(-1.0);
int main()
{
cin >> a >> r;
if (r <= sqrt(6.0) / 12.0 * a)
{
cout << 0;
}
else if(r <= a / (2.0 * sqrt(2.0)))//球和三棱锥框架相切,此时形状为四个圆形
{
cout << 4.0 * PI * (r * r - 1.0 / 24 * a * a);//注意这里是1.0 不能写成1!!!
}
else if(r <= sqrt(6.0) / 4.0 * a)//三棱锥内接于球
{
double rr = sqrt(r * r - 6.0 / 144 * a * a);
double h = a / (2.0 * sqrt(3));
double b = 2.0 * sqrt(rr * rr - h * h);
double tri = b * h * 0.5;
double sector = PI * rr * rr * (asin(b / (2 * rr)) * 2 / (2 * PI));
double each = PI * rr * rr - 3 * (sector - tri);
cout << 4 * each;
}
else
{
cout << sqrt(3) * a * a;
}
return 0;
}
F. 对答案一时爽
签到题。
#include <iostream>
using namespace std;
int n;
char a[105], b[105];
int main()
{
int mmax = 0, mmin = 0;
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
}
for(int i = 1; i <= n; i++)
{
cin >> b[i];
}
for(int i = 1; i <= n; i++)
{
if(a[i] == b[i])
{
mmax += 2;
}
else
{
mmax++;
}
}
cout << mmax << ' ' << mmin;
return 0;
}
I. 限制不互素对的排列
首先看到k的范围比较特殊,联想到奇偶性。然后知道相邻两个奇数互素,相邻一奇一偶互素,启发我们把前面搞成相邻偶数,后面搞成相邻奇数,类似2 4 6... 1 3 5... 可以发现前面有n / 2个偶数,可以贡献n / 2 - 1对,因此k < n /2的情况已经可以构造出来了,那么需要特判k = n /2的情况,最简单的写法是把6和最后一个偶数位置互换,1和3位置互换。(代码写的是1和9(存在的话)互换然后加特判,挫了)
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int n, k, a[100005];
int main()
{
cin >> n >> k;
for (int i = 1; i <= n; i++)
a[i] = i;
if(!k)
{
for (int i = 1; i <= n; i++)
cout << a[i] << ' ';
return 0;
}
if(n == 2)
{
cout << -1;
return 0;
}
if(n == 3 || n == 4 && k == 2 || n == 5 && k == 2)
{
cout << -1;
return 0;
}
if(n == 6 && k == 3)
{
cout << "2 4 6 3 1 5";
return 0;
}
if(n == 7 && k == 3)
{
cout << "2 4 6 3 1 5 7";
return 0;
}
if(n == 8 && k == 4)
{
cout << "8 2 4 6 3 1 5 7";
return 0;
}
if(k == n / 2)
{
for (int i = 1; i <= k; i++)
a[i] = 2 * i;
for (int i = k + 1; i <= n; i++)
a[i] = 2 * (i - k) - 1;
if (n >= 9)
swap(a[k + 1], a[k + 5]);
}
else
{
for (int i = 1; i <= k + 1; i++)
a[i] = 2 * i;
for (int i = 1; i <= k + 1; i++)
a[k + 1 + i] = a[i] - 1;
for (int i = 2 * k + 3; i <= n; i++)
a[i] = i;
}
for (int i = 1; i <= n; i++)
cout << a[i] << ' ';
return 0;
}
然后这个题可以拓展成最长gcd序列:
![截屏2021-02-02 下午11.21.44](/Users/xiezhengyuan/Library/Application Support/typora-user-images/截屏2021-02-02 下午11.21.44.png)
J. 一群小青蛙呱嘣呱嘣呱
首先观察要求最小公倍数的数有什么样的特点。2划掉了2的k次方,3划掉了3的k次方...因此这些数必须有两个以上的质因子。
而\(a=p_1^{a1}p_2^{a2}p_3^{a3}... b = p_1^{b1}p_2^{b2}p_3^{b3}\),联想唯一分解定理,则\(lcm(a, b) = p_1^{max(a1,b1)}p_2^{max(a2,b2)}p_3^{max(a3,b3)}...\)
因此考虑每个质数的贡献,即对于p, \(p^x\)的x最高能取到多少。对于2,为了使x最大化,只能:\(2^x \times 3 <= n\),因为取别的的话x就会变小。同理,其他素数p,只能:\(p^x \times 2 <= n\)。
#include <iostream>
#include <cmath>
#include <cstring>
#define maxn 160000005
#define mod 1000000007
using namespace std;
int v[maxn] = { 0 }, prime[maxn];
int n, m, mmax = 0;
int fpow(int a, int b)
{
int ans = 1;
for (; b; b >>= 1)
{
if(b & 1)
ans = ans * a % mod;
a = a * a % mod;
}
return ans;
}
void primes(int nn)
{
v[0] = 0;
m = 0;
for (int i = 2; i <= nn; i++)
{
if(v[i] == 0)
{
v[i] = 1ll * i;
prime[++m] = 1ll * i;
if(i <= n)
mmax = max(mmax, m);
}
for (int j = 1; j <= m; j++)
{
if(prime[j] > v[i] || prime[j] > 1ll * nn / i)
break;
v[i * prime[j]] = prime[j];
}
}
}
int main()
{
cin >> n;
primes(80000000);
long long ans = 1;
for (int i = 1; i <= mmax; i++)
{
long long pre = ans;
if (i == 1)
{
ans = ans * fpow(2, floor(log(n / 3) / log(prime[i]))) % mod;
}
else
{
ans = ans * fpow(prime[i], floor(log(n / 2) / log(prime[i]))) % mod;
}
if(ans == pre)
break;
//cout << ans << endl;
}
if(n < 6)
cout << "empty";
else
cout << ans;
return 0;
}