2021中国大学生程序设计竞赛(CCPC)- 网络选拔赛 题解分享1001/1006/1007/1009

题解分享

1001

这题是队友写的,结论挺好推的,直接放代码了。

#include<bits/stdc++.h>
using namespace std;
#define FOR(a,b,c) for(int a=b;a<=c;a++)
typedef long long ll;
typedef unsigned long long ull;
int mod =1e9+7;
int inf =(1<<31)-1;
ll ksm(int i,int j){
    if(i==0)return 1;
    ll ans=1;ll t=i;
    while(j){
        if(j&1)ans=ans*t%mod;
        t=t*t%mod;
        j>>=1;
    }return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        ll n;
        scanf("%lld",&n);
        ll ans=0;
        ll t=n/3;
        while(t*3<n)
        {
            t++;
        }
        if(t%2==0)t++;
        if(n%2==0)
            ans=(n)/2+(n-t+1)/2;
        else 
            ans=(n+1)/2+(n-t)/2+1;
        printf("%lld\n",ans);
    }
}

1006

这是一个构造题。
我们注意到对于任意的 i 都有(i+3)^ 2 - (i+2)^ 2 - (i+1)^ 2 + i^2=4,那么字符串“1001”就能产生4的贡献,所以当n%4=0时k个“1001”就能满足结果;当n%4=1时就构造“1”+k个“1001”;当n%4=2时就构造“0001”+k个“1001”;当n%4==3时就构造“01”+k个“1001”。(其中k=n/4向下取整)

附代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        scanf("%lld", &n);
        if (n == 1) {
            cout << 1 << endl << 1 << endl;
            continue;
        }
        if (n == 2) {
            cout << 4 << endl << "0001" << endl;
            continue;
        }
        if (n == 3) {
            cout << 2 << endl << "01" << endl;
            continue;
        }
        int k = 0;
        string ans;
        if (n % 4 == 0) {
            k = n;
            for (int i = 1; i <= n / 4; i++) {
                ans += "1001";
            }
        } else {
            int cnt = n / 4, last;
            last = n - cnt * 4;
            if (last & 1) {
                if (last == 1) {
                    ans += "1";
                    for (int i = 1; i <= cnt; i++) ans += "1001";
                    k = 1 + cnt * 4;
                } else {
                    ans += "01";
                    for (int i = 1; i <= cnt; i++) ans += "1001";
                    k = 2 + cnt * 4;
                }
            } else {
                ans += "0001";
                for (int i = 1; i <= cnt; i++) ans += "1001";
                k = cnt * 4 + 4;
            }
        }
        cout << k << endl << ans << endl;
    }
    return 0;
}

1007

拿到题目首先想到要将g(x)进行处理,因为如果去掉g(x)原来的函数就是一个二次函数,二次函数的极值点很好求(要么在定义域的边界,要么在对称轴嘛)。注意到N的范围是(1<=N<=1e6),那么1<=g(x)<=54,可以预处理处对于任意的1<=x<=N的g(x)值,然后再找出距离对称轴最近的满足g(x)=i的x(i是x的数位和)。

附代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
vector<int> g[60];
void init() {
    for (int i = 1; i <= 1000000; i++) {
        int temp = i, sum = 0;
        while (temp) {
            sum += temp % 10;
            temp /= 10;
        }
        g[sum].push_back(i);
    }
}
int fx(int a, int b, int x) { return a * x * x + b * x; }
signed main() {
    int t;
    cin >> t;
    for (int i = 1; i <= 54; i++) g[i].push_back(-1e9);
    init();
    for (int i = 1; i <= 54; i++) g[i].push_back(1e9);
    while (t--) {
        int a, b, c, d;
        scanf("%lld%lld%lld%lld", &a, &b, &c, &d);
        int n;
        scanf("%lld", &n);
        int res = 1e18;
        for (int x = 1; x <= 54; x++) {
            if (g[x][1] > n) continue;
            int l = 1,
                r = --upper_bound(g[x].begin(), g[x].end(), n) - g[x].begin();
            int A = a * x + b, B = c * x * x + d * x;
            res = min({res, fx(A, B, g[x][l]), fx(A, B, g[x][r])});
            //当A<=0时二次函数的极小值在定义域的两端取到,故不需要进行以下操作
            if (A > 0) {
                while (l <= r) {
                    int lmid = l + (r - l) / 3, rmid = r - (r - l) / 3;
                    int fl = fx(A, B, g[x][lmid]), fr = fx(A, B, g[x][rmid]);
                    if (fl <= fr)
                        r = rmid - 1;
                    else
                        l = lmid + 1;
                    res = min({res, fl, fr});
                }
            }
        }
        printf("%lld\n", res);
    }
    return 0;
}

1009

这题是我们队第一个ac的题,几乎是开题就有想法了。题意是说给了一个字符串,代表人行走的序列,问该字符串中有多少连续子串,使得人经过该子串的操作后能回到原点。首先进行所给字符串的操作得到一系列的坐标,其实答案就是对所有坐标中相等的坐标进行两两组合,比如(0,10)出现了10次,那么其对答案的贡献就是10*(10+1)/2。

附代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
struct node {
    int x, y;
    bool operator<(const node &a) const {
        if (x != a.x)
            return x < a.x;
        else
            return y < a.y;
    }
};
map<node, int> mp;
signed main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        scanf("%lld", &n);
        string s;
        cin >> s;
        mp.clear();
        int nowx = 0, nowy = 0;
        node pos;
        pos.x = nowx, pos.y = nowy;
        mp[pos] = 1;
        int ans = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s[i] == 'U')
                nowy++;
            else if (s[i] == 'D')
                nowy--;
            else if (s[i] == 'L')
                nowx--;
            else
                nowx++;
            pos.x = nowx, pos.y = nowy;
            if (!mp[pos])
                mp[pos] = 1;
            else
                ans += mp[pos], mp[pos]++;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

第一次写题解,如果有写得不好的地方请大家指出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值