2020牛客寒假算法基礎集訓營1 題解

知識點: 字符串,貪心,矩陣快速冪,概率論,計算幾何,並查集,數論

A題 honoka和格點三角形

紙上畫一畫,即可推出公式。
計算會出現重複,箭頭即爲存在衝突,這裏m-2就是去重,最後乘2,因爲四種情況,是兩兩對稱的。
在這裏插入圖片描述
在這裏插入圖片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
int main()
{
    ll n, m;
    scanf("%lld %lld", &n, &m);
    ll ans = 2 * (n + m - 2) % mod *( (n - 1) * (m - 2) % mod + (n - 2) * (m - 1) % mod) % mod;
    printf("%lld\n", ans);
    return 0;
}

B題 kotori和bangdream

水題

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int main()
{
    int n, x, a, b;
    scanf("%d %d %d %d", &n, &x, &a, &b);
    double ans = 0.01 * x * a * n + 0.01 * (100 - x) * b * n;
    printf("%.2f\n", ans);   
    return 0;
}

C題 umi和弓道

計算幾何,題目要求能射中的點小於等於k個,那麼我把k的值更新爲k = n - k,那麼目標就轉化爲在某個座標軸上用最短擋板擋住k個點。
分別存儲人與靶在x軸和y軸上的交點,然後分別在x軸和y軸上尺取k個交點,取最短距離即爲答案。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const ll mod = 1e9 + 7;

vector <double> v1, v2;//分別爲x0 y0 與點在 x軸和y軸上的交點

int main()
{
    double x0, y0;
    int n, k;
    cin >> x0 >> y0 >> n >> k;
    k = n - k;
    //目標轉換爲遮擋住k個點
    for(int i = 1; i <= n; i++)
    {
        double x, y;
        cin >> x >> y;
        if(x0 * x < 0)//y軸上有交點
        {
            v2.push_back(y0 - (y0 - y) * x0 / (x0 - x) );
        }
        if(y0 * y < 0)
        {
            v1.push_back(x0 - y0 * (x0 - x) / (y0 - y) );
        }
    }

    double mi = 1e18;
    sort(v1.begin(), v1.end());
    sort(v2.begin(), v2.end());

    if(v1.size() >= k)//至少有k個能被擋住
    {
        int l = 0, r = k - 1;
        while(r < v1.size())
        {
            mi = min(mi, abs(v1[r] - v1[l]) );
            l++, r++;
        }
    }
    if(v2.size() >= k)
    {
        int l = 0, r = k - 1;
        while(r < v2.size())
        {
            mi = min(mi, abs(v2[r] - v2[l]) );
            l++, r++;
        }
    }
    
    if(mi == 1e18)
        printf("-1\n");
    else
        printf("%.10f", mi);
    return 0;
}

D題 hanayo和米飯

水題

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int a[maxn];
int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 1; i < n; i++)
    {
        scanf("%d", &a[i]);
    }
    sort(a + 1, a + n);
    for(int i = 1; i <= n; i++)
    {
        if(a[i] != i)
            return 0 * printf("%d\n", i);
    }
    return 0;
}

E題 rin和快速迭代

按題意模擬即可,每次跑出所有的因數(跑到根號)。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int main()
{
    ll n;
    scanf("%lld", &n);
    ll ans = 0;
    ll cnt = n;
    while(cnt != 2)
    {
        ans++;
        ll tem = cnt;
        cnt = 0;
        for(ll i = 1; i * i <= tem; i++)
        {
            if(tem % i == 0)
                cnt += 2;
        }
        ll num = sqrt(tem);
        if(num * num == tem)
            cnt--;
    }
    printf("%lld\n", ans);
    return 0;
}

F題 maki和tree

在這裏插入圖片描述
思路是這樣的,但是當時不會代碼的實現
主要在於推出公式進行時間上的優化,t1 t2,一個是維護平方和,一個是維護和,和的平方 減去 平方和 後除以2,剛好就是乘積在這裏插入圖片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const ll mod = 1e9 + 7;
string color;
vector <int> g[maxn];
ll sum, ans, t1, t2;

void dfs(int p, int fa)
{
    if(color[p] == 'B')
        return ;
    sum++;//白點數量加1
    for(int i = 0; i < g[p].size(); i++)
    {
        if(g[p][i] != fa)//遍歷所有沒遍歷過的點
            dfs(g[p][i], p);
    }
}
int main()
{
    int n;
    cin >> n >> color;
    for(int i = 1; i < n; i++)
    {
        int x, y;
        scanf("%d %d", &x, &y);
        g[x].push_back(y);
        g[y].push_back(x);
    }
    for(int i = 1; i <= n; i++)
    {
        t1 = t2 = 0;
        if(color[i] == 'B')//如果是黑色,從黑色開始搜索
        {
            for(int j = 0; j < g[i].size(); j++)//遍歷與黑點連接的所有點
            {
                sum = 0;
                dfs(g[i][j], g[i][j]);//對白點進行深搜
                ans += sum;
                t1 += sum;
                t2 += sum * sum;
            }
        }
        ans += (t1 * t1 - t2) / 2;//公式化簡
    }
    printf("%lld\n", ans);
    return 0;
}

G題 eli和字符串

記錄每種字母的位置信息,在符合題意情況下,取位置差值的最小值。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
const int INF = 0x3f3f3f3f;
char s[maxn];
vector <int> pos[257];
int main()
{
    int n, k;
    scanf("%d %d", &n, &k);
    scanf("%s", s + 1);
    for(int i = 1; i <= n; i++)
    {
        char ch = s[i];
        pos[ch].push_back(i);
    }

    int ans = INF;
    for(int i = 1; i <= 256; i++)
    {
        if(pos[i].size() >= k)
        {
            for(int j = 0; j < pos[i].size() - k + 1; j++)
                ans = min(ans, pos[i][j + k - 1] - pos[i][j] + 1);
        }
    }
    if(ans == INF)
        printf("-1\n");
    else
        printf("%d\n", ans);
    return 0;
}

H題 nozomi和字符串

要麼只把0換成1,要麼只把1換成0,用兩個尺取獲取最大距離。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int maxn = 2e5 + 10;
char s[maxn];
int pre0[maxn];
int pre1[maxn];
int main()
{
    int n, k;
    scanf("%d %d", &n, &k);
    scanf("%s", s + 1);
    for(int i = 1; i <= n; i++)
    {
        if(s[i] == '0')
        {
            pre0[i] = pre0[i - 1] + 1;
            pre1[i] = pre1[i - 1];
        }
        else
        {
            pre1[i] = pre1[i - 1] + 1;
            pre0[i] = pre0[i - 1];
        }
    }
    //尺取0
    int L = 0, R = 1;
    int ans = 0;
    while(R <= n)
    {
        while(R <= n && pre0[R] - pre0[L] <= k)
        {
            ans = max(ans, R - L);
            R++;
        }
        L++;
    }
    //尺取1
    int L1 = 0, R1 = 1;
    int ans1 = 0;
    while(R1 <= n)
    {
        while(R1 <= n && pre1[R1] - pre1[L1] <= k)
        {
            ans1 = max(ans1, R1 - L1);
            R1++;
        }
        L1++;
    }
    printf("%d\n", max(ans, ans1));
    return 0;
}

I題 nico和niconiconi

一道dp題,其實不難。
轉移方程就是那麼的……樸實無華。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[322222]={0};
string a;
int main(){
 
    ll i,n,x,y,z;
    cin>>n>>x>>y>>z;
    cin>>a;
    for(i=0;i<n;i++){
        if(i>0)dp[i]=dp[i-1];
        if(i>=3&&a[i-3]=='n'&&a[i-2]=='i'&&a[i-1]=='c'&&a[i]=='o')
            dp[i]=max(dp[i],dp[i-3]+x);
        if(i>=5&&a[i-5]=='n'&&a[i-4]=='i'&&a[i-3]=='c'&&a[i-2]=='o'&&a[i-1]=='n'&&a[i]=='i')
            dp[i]=max(dp[i],dp[i-5]+y);
        if(i>=9&&a[i-9]=='n'&&a[i-8]=='i'&&a[i-7]=='c'&&a[i-6]=='o'&&a[i-5]=='n'&&a[i-4]=='i'&&a[i-3]=='c'&&a[i-2]=='o'&&a[i-1]=='n'&&a[i]=='i')
            dp[i]=max(dp[i],dp[i-9]+z);
    }
    //這裏偷懶了,對於字符a[i]='n'的情況,顯然dp[i]=dp[i-1]。這樣就不用處理dp[-1]的值。
    cout<<dp[n-1];
}

J題 u’s的影響力

這道題是在下面這道題的基礎上修改的: http://www.z4zr.com/page/450.html

比賽結束後樣例增強了,找了幾個榜上ak的人的代碼,他們都過不了。

賽後我調試了很久,終於過了。
在這裏插入圖片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const ll mod = 1e9 + 7;
ll n, x, y, a, b;
 
typedef vector <ll> vec;
typedef vector <vec> mat;
 
mat mul(mat &A, mat &B, ll mod)
{
    mat C(A.size(), vec(B[0].size()));
    for(int i = 0; i < A.size(); i++)
    {
        for(int k = 0; k < B.size(); k++)
        {
            for(int j = 0; j < B[0].size(); j++)
            {
                C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod;
            }
        }
    }
    return C;
}
 
mat mat_pow(mat A, ll n, ll mod)
{
    mat B(A.size(), vec(A.size() ) );
    for(int i = 0; i < A.size(); i++){
        B[i][i] = 1;
    }
    while(n > 0)
    {
        if(n & 1)
            B = mul(B, A, mod);
        A = mul(A, A, mod);
        n >>= 1;
    }
    return B;
}
 
ll quick_pow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1)
            ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
int main()
{
    cin >> n >> x >> y >> a >> b;
 
    x %= mod;
    y %= mod;
 
    if(n == 1)
    {
        return 0 * printf("%lld\n", x);
    }
    else if(n == 2)
    {
        return 0 * printf("%lld\n", y);
    }
    else
    {
        if(a % mod == 0 || x % mod == 0 ||y % mod == 0)
            return 0 * printf("0\n");
            
        mat A(2, vec(2));
 
        A[0][0] = 1, A[0][1] = 1;
        A[1][0] = 1, A[1][1] = 0;
 
        A = mat_pow(A, n - 2, mod - 1);
 
        ll power_x = quick_pow(x, A[1][0]) % mod;
        ll power_y = quick_pow(y, A[0][0]) % mod;
 
        mat B(3, vec(3));
 
        B[0][0] = B[0][1] = B[0][2] = B[1][0] = B[2][2] = 1;
 
        B = mat_pow(B, n - 4, mod - 1);
 
        a = quick_pow(a % mod, b) % mod;
 
        ll mi = ((B[0][0] * 2 % (mod - 1) + B[0][1]) % (mod - 1) + B[0][2]) % (mod - 1);
 
        printf("%lld\n", power_x * power_y % mod * quick_pow(a % mod, mi % (mod - 1) ) % mod);
    }
    return 0;
}
發佈了184 篇原創文章 · 獲贊 24 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章