1. 程式人生 > 其它 >第八屆“圖靈杯”NEUQ-ACM程式設計競賽個人賽

第八屆“圖靈杯”NEUQ-ACM程式設計競賽個人賽

技術標籤:ACM--比賽補題

文章目錄

第八屆“圖靈杯”NEUQ-ACM程式設計競賽個人賽

A 切蛋糕

題意: 現在有一個蛋糕,需要分給k個人,每次操作可以將一個蛋糕分為2份,還可以選擇一些蛋糕打包為一份,最後需要打包出k份,使得每一份的蛋糕量為1/k,誤差不大於1e-10,全部操作需要在6000步內完成‘。k保證不大於2e10

題解: 直接將所有的蛋糕分為1/1e-10,這樣需要的運算元為2e11-1,也就是2047次,然後打包出k份,操作次數不超過2e10,總體不超過2047+1024次,小於6000次

程式碼:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
int const MAXN = 2e5 + 10;
int n, m, T;
int two[15];
void init()
{
    two[0] = 1;
    for (int i = 1; i < 13; i++)
    {
        two[i]
= 2 * two[i - 1]; } } int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cin >> n; init(); cout << two[11] - 1 + n << endl; int cnt = 0; for (int i = 0; i <= 10; i++) { for (int j = 0; j < two[i]; j++) { cout <<
1 << " " << i << endl; } } double ans = 1.0 / n int num = ans / (1.0 / two[11]); for (int i = 0; i < n; i++) { cout << 2 << " " << num; for (int j = 0; j < num; j++) cout << " " << 11; cout << endl; } return 0; }

B 小寶的幸運陣列

題意: 給出一個長度為n的陣列,要求找到一個區間使得區間和為k的倍數,如果有的話輸出最長的區間長度,否則輸出-1

題解: 記錄字首和出現的最早位置,然後相減即可,需要每次都模k,這樣保證區間和是k的倍數

程式碼:

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int t, n, r;

LL sum[N], a[N], k;
map<LL, int> mp;
int main() {
    cin >> t;
    while (t--) {
        cin >> n >> k;
        mp.clear();
        int res = -1;
        mp[0] = 0;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            sum[i] = (sum[i - 1] + a[i] % k) % k;
            if(mp.count(sum[i])){
                res = max(res, i - mp[sum[i]]);
            }
            else
                mp[sum[i]] = i;
        }
        cout << res << endl;
    }
    return 0;
}

C 上進的凡凡

題意: 給出長度為n的陣列,求出其中不下降子陣列的數量

題解: 直接掃一遍,如果遇到長度為k的不下降子陣列,那麼答案應該加上1到k的累加和

程式碼:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
int const N = 2e5 + 10;
LL n, m, T;
LL a[N];
LL jie[N+10];
void init(){
    jie[0]=jie[1]=1;
    for(int i=2;i<=N;i++){
        jie[i]=jie[i-1]+i;
    }
}
int main() {
    cin>>n;
    init();
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    LL num=0;
    LL ans=0;
    for(int i=1;i<=n;i++){
        if(a[i]>=a[i-1]) num++;
        else{
            ans+=jie[num];
            num=1;
        }
    }
    ans+=jie[num];
    cout<<ans<<endl;
    return 0;
}

D Seek the Joker I

題意: 一共n個數的堆,從上到下開始取,每次最少取1個,最多取k個,取到最後一個的為輸

先手贏輸出yo xi no forever!

後手贏輸出ma la se mi no.1!

題解: 巴什博弈裸題

程式碼:

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int t, n, k;
int main(){
    cin >> t;
    while(t--){
        cin >> n >> k;
        if (n % (k + 1) == 1) cout << "ma la se mi no.1!" << endl;
        else
            cout << "yo xi no forever!" << endl;
    }
    return 0;
}

E Seek the Joker II

題意: 一個n個數的堆,每次可以從上面取任意個數,或者從下面取任意個數,或者同時從上面和下面取相同多個數,取到原來第x個的為輸。先手贏輸出yo xi no forever!。後手贏輸出ma la se mi no.1!

題解: 威佐夫博弈裸題

程式碼:

#include <bits/stdc++.h>
using namespace std;
int T;
int main() {
    int n1,n2,temp, n, x;
    cin >> T;
    while(T--) {
        cin >> n >> x;
        n1 = x - 1, n2 = n - n1 - 1;
        if(n1>n2)  swap(n1,n2);
        temp=floor((n2-n1)*(1+sqrt(5.0))/2.0);
        if(temp==n1) cout<<"ma la se mi no.1!"<<endl;
        else cout<<"yo xi no forever!"<<endl;
    }
    return 0;
}

F 成績查詢ing

題意: 給出n個人的學號、成績、性別、姓名。然後是m次查詢,如果輸入的是成績,則輸出全部的成績為這個成績的學生的姓名(按字典序)。如果查詢的是姓名,則輸出學生的成績

題解: 直接模擬即可,但是由於n和m比較大,直接查詢會超時,但是由於成績的範圍都是1到100,所以可以首先預處理出1到100分的答案,查詢的時候直接輸出即可

程式碼:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
typedef pair<int, PII> PIII;
int const N = 1e5 + 10;
int n, m;
unordered_map<string,PIII> mp;
unordered_map<int,set<string>> grade_name;
string g[N];
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin >> n;
    for(int i=0;i<n;i++){
        string name;
        int grade,sex,num;
        cin>>name>>grade>>sex>>num;
        PII sex_num={sex,num};
        mp[name]={grade,sex_num};
        grade_name[grade].insert(name);
    }
    for(auto it=grade_name.begin();it!=grade_name.end();it++){
        string s="";
        for(auto i=it->second.begin();i!=it->second.end();i++){
            s=s+(*i);
            s+="\n";
        }
        g[it->first]=s;
    }
    cin>>m;
    while(m--){
        int t;
        cin>>t;
        if(t==1){
            string name;
            cin>>name;
            //PIII gsn = mp[name];
            cout<<mp[name].first<<" "<<mp[name].second.second<<" "<<mp[name].second.first<<endl;
        }
        else{
            int grade;
            cin>>grade;
            cout<<g[grade];
        }
    }
    
    return 0;
}

G 貪吃的派蒙

題意: n個人,k份蛋糕。每個人都可以有一個飯量ai,每次輪到一個人的時候,可以取1到ai份蛋糕。但是這n個人裡面有一個貪心者派蒙,他是ai最大的那個人,他一定會取走ai份飯。所以現在大家想讓他刷盤子,也就是最後一個取完蛋糕,問能否使他最後一個取完全部的蛋糕。注意取完n個人一輪後,如果還有蛋糕,則從第1個人開始取第二輪

題解: 直接每一輪模擬即可,從左到右算最小值和最大值,看能否將派蒙包含進裡面即可

程式碼:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
LL n, k, t;
queue<LL> q;
int main() {
    cin >> t;
    while (t--) {
        cin >> n >> k;
        LL p = 0;
        while(!q.empty()) q.pop();
        for (int i = 0; i < n; i++) {
            LL x;
            cin >> x;
            p = max(p, x);
            q.push(x);
        }
        LL l = 0, r = 0;
        while (1) {
            LL x = q.front();
            q.pop();

            if (x == p) {
                r += x;
                if (k > l && k <= r) {
                    cout << "YES\n";
                    break;
                }
                if (k <= l) {
                    cout << "NO\n";
                    break;
                }
                l += x;
            } else {
                l++;
                r += x;
            }
            q.push(x);
        }
    }

    return 0;
}

H 數羊

題意: 給出n和m的值,求A(n,m):

img

題解: 先暴力打表,然後找出規律即可:

程式碼:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
int const MAXN = 2e5 + 10;
LL n, m, T;

LL qmi(LL a, LL k, LL p) {
    LL res = 1 % p;  // res記錄答案, 模上p是為了防止k為0,p為1的特殊情況
    while(k) {  // 只要還有剩下位數
        if (k & 1) res = (LL)res * a % p;  // 判斷最後一位是否為1,如果為1就乘上a,模上p, 乘法時可能爆int,所以變成long long
        k >>= 1;  // 右移一位
        a = (LL) a * a % p;  // 當前a等於上一次的a平方,取模,平方時可能爆int,所以變成long long
    }
    return res;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin >> T;
    while(T--) {
        cin >> n >> m;
        if (n == 1) cout << 2 << endl;
        else if (n == 2) cout << 4 << endl;
        else if (!m) cout << (n + 2) % 998244353 << endl;
        else if (m == 1) cout << n * 2 % 998244353 << endl;
        else cout << 8 * qmi(2, n - 3, 998244353) % 998244353 << endl;
    }
    return 0;
}

I 買花

題意: n個花,分k天買(k>1,即不能一天全買完),第一天可以買任意朵花,之後每一天買花的數量為前一天的兩倍。

問能否在15天內買夠n朵花

題解: 設第一天買的花為x個,那麼買2天就是買2x+x個,買3天就是買4x+2x+x個…

所以可以列舉買2到15天的情況,也就是列舉能買到x的倍數,然後看n能否整除某個倍數即可

程式碼:

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int t, n;
int main() {
    cin >> t;
    while (t--) {
        cin >> n;
        int flag = 0;
        for (int i = 2; i <= 15; i++) {
            if (n % ((1 << i) - 1) == 0) {
                flag = 1;
            }
        }
        if (flag) cout << "YE5" << endl;
        else
            cout << "N0" << endl;
    }
    return 0;
}

J 這是一題簡單的模擬

題意: 從0號點出發到n個城市,然後返回0號點

現在給出所有的路徑,以及k個方案

問花費最少的方案的花費是多少

題解: 直接模擬即可…

程式碼:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
int const MAXN = 3e2 + 10;
int n, m, k;
int g[MAXN][MAXN];
unordered_map<int, int> mp;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin >> n >> m;
    memset(g, -1, sizeof g);
    for (int i = 1, a, b, c; i <= m; ++i) {
        cin >> a >> b >> c;
        g[a][b] = g[b][a] = c;
    }
    cin >> k;
    LL res = 1e18;
    while(k--) {
        int t;
        cin >> t;
        int curpos = 0;
        LL tmp = 0, flg = 1;
        mp.clear();
        for (int i = 1, p; i <= t; ++i) {
            mp[curpos]++;
            // cout << "cur: " << curpos << endl;
            cin >> p;
            if (g[curpos][p] != -1) {
                tmp += g[curpos][p];
                curpos = p;
            }
            else {
                flg = 0;
            }
        }
        mp[curpos]++;
        if (g[curpos][0]) tmp += g[curpos][0];
        else flg = 0;
        for (int i = 1; i <= n; ++i) if (mp[i] != 1) {
            flg = 0;
            // cout << "ev: " << i << endl;
        }
        if (flg) {
            res = min(res, (LL)tmp);
        }
    }
    if (res != 1e18) cout << res << endl;
    else cout << -1 << endl;
    return 0;
}

K 黑洞密碼

題意: 輸入一個字串,然後進行如下操作:

1.確定訊息的長度為32;

2.字串中第 4 n + 1 ∼ 4 n + 4 4n+1\sim4n+4 4n+14n+4的字母和第 4 n + 1 ∼ 4 n + 4 4n+1\sim4n+4 4n+14n+4的數字為一組,共4組;

3.每組的第1,2,3,4個字元分別往後推每組第1,2,3,4個數字個數 例:如第一個字母為a,第一個數字為3,轉換後變為d,‘z’之後是’B’,‘Z’之後是’b’;

4.將每組內部字母的順序顛倒;

5.將四組字符合並就是最後的訊息。

題解: 直接模擬,但是需要注意的是,題目中說了z如果往後移動1位那麼會變為B,Z移動一位會變為b,比較坑

程式碼:

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
string s;
char ch[50], num[50];
string biao =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
int main() {
    cin >> s;
    int cnt1 = 0, cnt2 = 0;
    for (int i = 0; i < s.size(); i++) {
        if (s[i] >= '0' && s[i] <= '9')
            num[++cnt1] = s[i];
        else
            ch[++cnt2] = s[i];
    }
    string res = "";
    for (int i = 0; i <= 3; i++) {
        string temp = "";
        for (int j = 1; j <= 4; j++) {
            char now;
            if(ch[i * 4 + j]>='a'&&ch[i * 4 + j]<='z'){
                now = biao[ch[i * 4 + j]-'a' + num[i * 4 + j] - '0'];
                if (now >= 'A' && now <= 'Z') now++;
            }
            else {
                now = biao[ch[i * 4 + j]-'A'+26 + num[i * 4 + j] - '0'];
                if (now >= 'a' && now <= 'z') now++;
            }
            temp.push_back(now);
        }
        //cout << temp << endl;
        reverse(temp.begin(),temp.end());
        res = res + temp;
    }
    cout << res << endl;
    return 0;
}

L 建立火車站

題意: 給出n個城市,在這些城市之間可以建立k個車站,問最遠的兩個車站的最小值是多少(城市也需要看做車站)

需要注意車站位置必須是整數。

題解: 首先將每個區間的距離扔到優先佇列裡,然後每次都將隊頭的元素多加一個車站,然後再扔到優先佇列裡即可

程式碼:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<double, double> PII;
int const N = 2e5 + 10;
int n, k;
double a[N];
priority_queue<PII, vector<PII>, less<PII> > q;
int main() {
    cin >> n >> k;
    cin >> a[1];
    for (int i = 2; i <= n; i++) {
        cin >> a[i];
    }
    sort(a + 1, a + 1 + n);
    for (int i = 2; i <= n; i++) {
        q.push({a[i] - a[i - 1], 1});
    }
    while (k--) {
        PII t = q.top();
        double num = t.second;
        double p = t.first * num;
        q.pop();

        q.push({p / (num + 1), num + 1});
    }
    cout << ceil(q.top().first) << endl;
    //printf("%lld\n", ceil(q.top().first));
    return 0;
}