Loading

2021牛客寒假算法基础集训营1 补题 ABCEFIJ

https://ac.nowcoder.com/acm/contest/9981#question

A. 串

考虑先搞出来一个串然后逐渐添加。设dp[i]为长度为i的含有us子串的串的个数,则当dp[i - 1]已经求出来的情况下,第i个位置:

  1. 前面已经有us,第i个位置随便添加

    dp[i] += dp[i - 1] * 26;

  2. 前面没有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,在回溯的时候:

  1. 这个点是叶子结点:标记其与父亲节点同色
  2. 这个点不是叶子结点同时没有被其儿子结点标记:标记其与其父亲节点同色。

如果一个点没有被涂色但其父亲节点被涂色:无解

如果没有儿子结点能把整棵树的根结点涂色(即除了根结点外都配对):无解

#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;
}
posted @ 2021-02-07 23:44  脂环  阅读(102)  评论(0编辑  收藏  举报