第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛(同步赛)

A.切蛋糕
题解:将大小为1的蛋糕看成x份,那么x/k份能整除为x/k份,不能为x/k+1份,减去1/k即减去x/k份,那么前后相减,差值的绝对值为零或1份,而这个差值要<=1/2 ^ 10 ,也就是1份要<=1/2 ^ 10。 取1份=1/2 ^10,那么x=1024。分成1024份需要切2 ^ 0 + 2 ^ 1 + … + 2 ^ 9 = 1023次,打包需要k次,最多2047次。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int k, i, j, t = 1;cin >> k;
    cout << 1023 + k << endl;
    for (i = 0; i <= 9; i++){//每次只能切一块,1个0,2个1,4个2,...
        for (j = 0; j < t; j++)
            cout << 1 << ' ' << i << endl;
        t *= 2;
    }
    int cnt1 = 1024 / k, cnt2 = 1024 % k;
    for (i = 1; i <= k; i++){
        cout << 2;
        if (i <= cnt2){//如果1024不能被k整除,那就把余数一一加到前面,即前面的人多一份
            cout << ' ' << cnt1 + 1;
            for (j = 1; j <= cnt1 + 1; j++)
                cout << ' ' << 10;
        }
        else{
            cout << ' ' << cnt1;
            for (j = 1; j <= cnt1; j++)
                cout << ' ' << 10;
        }
        cout << endl;
    }
}

B.小宝的幸运数组
题解:设pre[i]为前i项和,[l,r]的区间和为pre[r]-pre[l-1]。如果数组和被k整除,那么(pre[r] - pre[l-1])%k=0,展开pre[r]%k=pre[l-1]%k。所以记录pre[l-1]%k的位置,当pre[r]%k的值和前面相同时,更新它们之间的距离,取最大值。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[100010], vis[100010];
ll pre[100010];
int main()
{
    int t, n, k, i;cin >> t;
    while (t--){
        memset(vis, -1, sizeof(vis));
        vis[0] = 0;
        int mx = -1;
        cin >> n >> k;
        for (i = 1; i <= n; i++){
            cin >> a[i];
            pre[i] = (pre[i - 1] + a[i]) % k;
            if (vis[pre[i]] == -1) vis[pre[i]] = i;//pre[l-1]即为某一值的初始位置
            else mx = max(mx, i - vis[pre[i]]);//pre[r]用来更新某一值的最大距离
        }
        cout << mx << endl;
    }
}

C.上进的凡凡

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[100010];
int main()
{
    int n, i;cin >> n;
    for (i = 1; i <= n; i++)
        cin >> a[i];
    ll l = 1, res = 0;
    for (i = 2; i <= n; i++){
        if (a[i - 1] <= a[i]) l++;
        else{
            res += l * (l + 1) / 2;
            l = 1;
        }
    }
    res += l * (l + 1) / 2;
    cout << res << endl;
}

D.Seek the Joker I
题解:巴什博弈:有n个东西,两个人拿,每轮每人至少拿一个最多拿m个,拿到最后一个赢。如果n % (m + 1)= 0,先手败。设n=m+1,那么无论先手拿几个都拿不完,且剩余东西大于等于1、小于等于m,所以后手一下拿完,先手败。当n=x(m + 1)时,重复x轮上述操作,还是先手败。这题拿到最后一张牌输,也就是拿到倒数第二张牌赢,即如果(n - 1)% (k + 1)=0,先手败。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t, n, k;cin >> t;
    while (t--){
        cin >> n >> k;
        if ((n - 1) % (k + 1)) cout << "yo xi no forever!" << endl;
        else cout << "ma la se mi no.1!" << endl;
    }
}

E. Seek the Joker II
题解:威佐夫博弈:有两堆东西,两个人拿,每轮每人可以在任意一堆里至少拿一个东西或在两堆里拿相同个东西,拿完最后一个的人获胜。两堆东西的数量分别为a、b,记作(a,b)(a<b),称为局势。当(b-a)*(√5+1)/2=a时,称这个局势为奇异局势,奇异局势先手败。非奇异这题上方有x-1张牌,下方有n-x张牌,上下方拿完后,下一轮轮到的人拿到乌龟输,即最后一个拿完上下方的牌的人赢。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t, n, x;cin >> t;
    while (t--){
        cin >> n >> x;
        int a = x - 1, b = n - x;
        if (a > b) swap(a, b);
        double c = (sqrt(5) + 1) / 2;
        int d = (b - a) * c;
        if (d == a) cout << "ma la se mi no.1!" << endl;
        else cout << "yo xi no forever!" << endl;
    }
}

F.成绩查询ing
题解:关于输出的一个问题:endl是在结束处插入新行并清缓冲区,’\n’是换行符,’\n’的效率高于endl。关键在于STL的使用。map对应姓名、成绩、性别、学号,set存放成绩相同的姓名并默认升序排列。以及容器通过迭代器遍历,方式如下:

  1.容器类型<数据类型>::iterator it;
  for (it = 容器名称.begin(); it != 容器名称.end(); it++)
      cout << *it << endl;
  //e.g. 
  set<int>s;
  set<int>::iterator it;
  for (it = s.begin(); it != s.end(); it++)
      cout << *it << endl;
2.for (容器类型<数据类型>::iterator it = 容器名称.begin(); it != 容器名称.end(); it++)
      cout << *it << endl;
  //e.g.
  for (set<int>::iterator it = s.begin(); it != s.end(); it++)
      cout << *it << endl;
#include<bits/stdc++.h>
using namespace std;
int main()
{
    set<string>s[110];
    map<string,int>mp1;
    map<string,int>mp2;
    map<string,int>mp3;
    string name, str;
    int grade, sex, id, score;
    int n, m, t;
    cin >> n;
    while (n--){
        cin >> name >> grade >> sex >> id;
        s[grade].insert(name);
        mp1[name] = grade;
        mp2[name] = sex;
        mp3[name] = id;
    }
    cin >> m;
    while (m--){
        cin >> t;
        if (t == 1){
            cin >> str;
            cout << mp1[str] << ' ' << mp3[str] << ' ' << mp2[str] << '\n';
        }
        else{
            cin >> score;
            for (set<string>::iterator it = s[score].begin(); it != s[score].end(); it++)
                cout << *it << '\n';
        }
    }
}

G.贪吃的派蒙
题解:轮数越多,派蒙就越有可能刷盘子。因为大家想让派蒙刷,轮数越多,大家吃的就越多。而轮数要多,一轮中吃的应该最少,即n-1+Ax个。设派蒙位置为i,如果前i-1个人吃最少,都吃一个即i-1个还比k大的话,那在派蒙之前就有人刷盘子了。后i+1个人最多剩余 (总数减去(i-1)减去Ax)个,计算剩余数从派蒙开始最多有几轮,多余的轮数直接吃最多的,最后一轮在已经吃一个的基础上加上最多能再吃几只。比较这个可吃部分和剩余部分即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[100010], pre[100010];
int main()
{
    int t, n, k, i;cin >> t;
    while (t--){
        cin >> n >> k;
        int mx = 0, x;
        for (i = 1; i <= n; i++){
            cin >> a[i];
            pre[i] = pre[i - 1] + a[i];
            if (a[i] > mx) mx = a[i], x = i;
        }
        if (k < x - 1) cout << "NO" << endl;
        else{
            int mi = n - 1 + mx;//一轮中吃最少的数量
            int rest = (k - (x - 1)) % mi;//剩余部分
            int round = (k - (x - 1)) / mi;//轮数
            ll get = (pre[n] - mx) * round;//多余轮数取最大的
            get += pre[x - 1] - (x - 1);//在1的基础上还能再最多吃几只
            get >= rest ? cout << "YES" << endl : cout << "NO" << endl;//可选部分>=剩余部分,可选部分取和剩余部分相同就满足条件
        }
    }
}

H.数羊
题解:枚举m的三种情况。m=0,m=1,m=2。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod = 998244353;
ll qp(ll x, ll y)
{
    ll res = 1;
    while (y){
        if (y & 1) res *= x, res %= mod;
        x *= x, x %= mod;
        y /= 2;
    }
    return res;
}
int main()
{
    int t;ll n, m;cin >> t;
    while (t--){
        cin >> n >> m;
        if (m == 0){
            if (n == 1) cout << 2 << endl;
            else cout << n + 2 << endl;
        }
        else if (m == 1) cout << 2 * n << endl;
        else cout << qp(2, n) << endl;
    }
}

I.买花
题解:注意输出。

#include<bits/stdc++.h>
using namespace std;
int quickpower(int x, int y)
{
    int res = 1;
    while (y){
        if (y & 1) res *= x;
        x *= x, y /= 2;
    }
    return res;
}
int main()
{
    int t, n, i;cin >> t;
    while (t--){
        cin >> n;
        int flag = 0;
        for (i = 2; i <= 15; i++){
            int num = quickpower(2, i) - 1;
            if (n % num == 0){
                flag = 1;
                break;
            }
        }
        flag ? cout << "YE5" << endl : cout << "N0" << endl;
    }
}

J.这是一题简单的模拟

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int dis[310][310];
int x[610], mp[310];
int main()
{
    int n, m, u, v, w, k, nn, i;cin >> n >> m;
    while (m--){
        cin >> u >> v >> w;
        dis[u][v] = dis[v][u] = w;
    }
    int mi = -1;
    cin >> k;
    while (k--){
        map<int,int>mp;
        int sum = 0, flag = 1, cnt = 0;
        cin >> nn;
        for (i = 1; i <= nn; i++)
            cin >> x[i], mp[x[i]]++;
        for (i = 1; i <= nn; i++)
            if (mp[x[i]] == 1) cnt++;
        for (i = 1; i <= nn; i++){
            if (!dis[x[i - 1]][x[i]]){
                flag = 0;
                break;
            }
            sum += dis[x[i - 1]][x[i]];
        }
        if (!dis[x[nn]][0] || cnt != n) flag = 0;//判断最后一个点到0是否有边以及有无点重复、遗漏
        sum += dis[x[nn]][0];
        if (flag){
            if (mi == -1) mi = sum;
            else mi = min(mi, sum);
        }
    }
    cout << mi << endl;
}

K.黑洞密码

#include<bits/stdc++.h>
using namespace std;
char s[40];
char s1[20], s2[20];
int main()
{
    cin >> s + 1;
    int i, j = 0, k = 0;
    for (i = 1; i <= 32; i++){
        if (s[i] >= '0' && s[i] <= '9') s2[++k] = s[i];
        else s1[++j] = s[i];
    }
    for (i = 1; i <= 4; i++)
    for (j = 4 * (i - 1) + 4; j >= 4 * (i - 1) + 1; j--){
        if (s1[j] >= 'a' && s1[j] <= 'z'){
            if (s1[j] + s2[j] - '0' > 'z') printf("%c", 'A' + s2[j] - '0' - ('z' - s1[j]));
            else printf("%c", s1[j] + s2[j] - '0');
        }
        else{
            if (s1[j] + s2[j] - '0' > 'Z') printf("%c", 'a' + s2[j] - '0' - ('Z' - s1[j]));
            else printf("%c", s1[j] + s2[j] - '0');
        }
    }
    printf("\n");
}

L. 建立火车站
题意:最多能建k个站台,求相邻两站台间最小的最大距离。
题解:设最大距离为L,如果此时要建的站台数<=k,那么L合法。二分查找这个最小的L。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[100010], dis[100010];
int n, k, i;
bool check(ll l)
{
    ll sum = 0;
    for (i = 2; i <= n; i++){
        ll num = dis[i] / l;
        if (dis[i] % l) num++;
        num--;
        sum += num;
    }
    return sum <= k;
}
int main()
{
    map<ll,int>mp;
    cin >> n >> k;
    for (i = 1; i <= n; i++)
        cin >> a[i], mp[a[i]]++;
    sort(a + 1, a + 1 + n);
    for (i = 2; i <= n; i++)
        dis[i] = a[i] - a[i - 1];
    ll l = 1, r = 1e12, mid, ans;
    while (l <= r){
        mid = (l + r) / 2;
        if (check(mid)) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    cout << ans << endl;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值