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

https://ac.nowcoder.com/acm/contest/11746
好久没写过代码了,思维能力下降,爬了

A. 切蛋糕

待补

B. 小宝的幸运数组

套路题,初始化tmp为0,从头遍历一遍数组,用tmp加上当前a[i]再对k取模,如果有两个位置得到的模数一样,说明这一段的和就是k的倍数。因为要求最长的子列,注意到k的范围只有1e5,因此只需要用一个桶记录某个得到的模数最开始出现的位置即可。

#include <iostream>
#define int long long
using namespace std;
int n, k, a[200005];
int pre[200005] = {-1};
signed main()
{
    int t;
    cin >> t;
    while(t--)
    {
        cin >> n >> k;
        long long tot = 0;
        for (int i = 0; i <= 100005; i++)
            pre[i] = -1;
        for (int i = 1; i <= n; i++)
        {
            cin >> a[i];
            tot += a[i];
        }
        if(n == 1)
        {
            if(a[1] == k) cout << 1 << endl;
            else cout << -1 << endl;
            continue;
        }
        if(k == 1|| tot % k == 0)
        {
            cout << n << endl;
            continue;
        }
        int tmp = 0;
        int ans = -1;
        a[0] = 0;
        for (int i = 0; i <= n; i++)
        {
            tmp = (tmp + a[i]) % k;
            //cout << tmp << ' ';
            if (pre[tmp] == -1)
                pre[tmp] = i;
            else
            {
               //cout << tmp << ' ' << pre[tmp] << endl;
                ans = max(ans, i - pre[tmp]);
         
            }
        }
        //cout << endl;

        cout << ans << endl;
    }
    return 0;
}

C. 上进的凡凡

dp水题。dp[i]代表以a[i]结尾的非降连续子数组的个数,有转移方程: dp[i] += (a[i] >= a[i - 1]) ? dp[i - 1] : 0;

统计答案即可。

#include <iostream>
using namespace std;
long long n, a[100005], dp[100005];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        dp[i] = 1;
    }
    long long ans = 0;
    for (int i = 1; i <= n; i++)
    {
        if(a[i] >= a[i - 1])
            dp[i] += dp[i - 1];
        ans += dp[i];
    }
    cout << ans;
    return 0;
}

D. Seek the Joker I

巴什博弈(比赛的时候脑补的)。先手最优策略是等后手拿x张以后选择拿k + 1 - x张,这样相当于拿了有限组的k + 1张,用n % (k + 1) 得到剩下的,这时候先手只要在第一次拿的时候拿走n % (k + 1) - 1张,这样最终会留下一张小丑给后手。如果没法这样拿的话说明先手必败。

#include <iostream>
using namespace std;
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        int n, k;
        cin >> n >> k;
        int tmp = n % (k + 1);
        if(tmp > 1 || tmp == 0)
            cout << "yo xi no forever!" << endl;
        else
            cout << "ma la se mi no.1!" << endl;
    }
    return 0;
}

E. Seek the Joker II

威佐夫博弈,不会QAQ

F. 成绩查询ing

据说可以map瞎搞

G. 贪吃的派蒙

H. 数羊

估计是改编自HDU1165。直接暴力不可取,但注意到m范围只有0,1,2,因此考虑求出来一部分公示缩减递归的规模,比如可以求出来m==1的情况,剩下的再递归就可以了。答案把所有公式都写出来了也可以。

#include <iostream>
#define p 998244353
using namespace std;
long long n, m;
long long A(long long n, long long m)
{
    if(n == 1 && m == 0)
        return 2;
    if(n == 0)
        return 1;
    if(m == 0)
        return (n % p + 2) % p;
    if(n == 1 && m == 1)
        return 2;
    if(m == 1)
        return 2 * n;
    return A(A(n - 1, m), m - 1) % p;
}
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        cin >> n >> m;
        cout << A(n, m) % p << endl;
    }
    return 0;
}

I. 买花

可以随便瞎搞,我选择求一个前缀和然后枚举子数组判断和能否整除n。

#include <iostream>
#define int long long
using namespace std;
int a[105], sum[105];
signed main()
{
	//freopen("data.txt", "r", stdin);
	a[1] = 1;
	sum[1] = 1;
	for(int i = 2; i <= 20; i++)
	{
		a[i] = a[i - 1] * 2;
		sum[i] = sum[i - 1] + a[i];
	}
	int t;
	cin >> t;
	while(t--)
	{
		int n;
		cin >> n;
		bool flag = 0;
		for(int i = 1; i <= 15; i++)
		{
			for(int j = i + 1; j <= 15; j++)
			{
				int s = sum[j] - sum[i - 1];
				if(n % s == 0) 
				{
					flag = 1;
					break;
				}
			}
		}
		if(flag) cout << "YE5" << endl;
		else cout << "N0" << endl;
	}
	return 0;
}

J. 这是一道简单的模拟

题目说的没错。开二维数组写即可。

#include <iostream>
#include <vector>
#define N 305
#define M 10005
using namespace std;
int n, m, k;
// k, head[N], ver[2 * M], edge[2 * M], Next[2 * M], tot = 0;
// void add(int x, int y, int z)
// {
//     ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
// }
int mmap[305][305] = {0};
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        //add(x, y, z);
        //add(y, x, z);
        mmap[x][y] = mmap[y][x] = z;
    }
    cin >> k;
    long long cost = 1e15;
    for (int i = 1; i <= k; i++)
    {
        int num;
        cin >> num;
        vector<int> v;
        v.push_back(0);
        long long cc = 0;
        bool flag = 1, vis[305] = { 0 };
        for (int i = 1; i <= num; i++)
        {
            int p;
            cin >> p;
            v.push_back(p);
        }
        v.push_back(0);
        for (int i = 1; i < v.size(); i++)
        {
            vis[v[i]] = 1;
            if (!mmap[v[i]][v[i - 1]]) 
            {
                flag = 0;
                break;
            }
            cc += 1ll * mmap[v[i]][v[i - 1]];
        }
        for (int i = 1; i <= n; i++) 
        {
            if(!vis[i])
            {
                flag = 0;
                break;
            }
        }
        if(flag)
            cost = min(cost, cc);
    }
    if(cost != 1e15) cout << cost;
    else
        cout << -1;
    return 0;
}

K. 黑洞密码

模拟即可,注意循环后移和平时不同,由于最多往后移动九次,因此按照下述代码搞蛮方便的。

#include <iostream>
#include <vector>
using namespace std;
string s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZbcdefghijklmn";
string s2 = "abcdefghijklmnopqrstuvwxyzBCDEFGHIJKLMN";    
char v1[400];
int v2[400];
char ans[400];
int main()
{
    //freopen("data.txt", "r", stdin);
    string s;
    cin >> s;
    int pos1 = 0, pos2 = 0;
    for (int i = 0; i < s.size(); i++)
    {
        if(s[i] >= '0' && s[i] <= '9')
            v2[pos2++] = s[i] - '0';
        else
            v1[pos1++] = s[i];
    }
    
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            char tmp = v1[i * 4 + j];
            if (tmp >= 'a' && tmp <= 'z')
            {
                ans[i * 4 + j] = s2[(int)(tmp - 'a') + v2[i * 4 + j]];
            }
            else
            {
                ans[i * 4 + j] = s1[(int)(tmp - 'A') + v2[i * 4 + j]];
            }
        }
    }
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 2; j++)
        {
            swap(ans[i * 4 + j], ans[i * 4 + 3 - j]);
        }
    }
    for(int i = 0; i < 16; i++) cout << ans[i];
}

L. 建立火车站

看到最大距离最小想到二分,直接对答案区间进行二分,check的时候判断每一段区间能否用现有的k个停靠站分成最大不超过mid的子区间,能的话更新k同时判断下一个区间,不能的话return false。

#include <iostream>
#include <vector>
#include <algorithm>
#define int long long
using namespace std;
long long n, k, pos[100005];
vector<long long> v;
bool check(long long mid)
{
    long long kk = k;
    for (int i = 0; i < v.size(); i++)
    {
        int num = v[i] / mid;
        if(v[i] % mid != 0)
            num++;
        num--;
        if(kk < num)
        {
            return false;
        }
        kk -= num;
    }
    return true;
}
signed main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
        cin >> pos[i];
    sort(pos + 1, pos + n + 1);
    for (int i = 2; i <= n; i++)
        v.push_back(pos[i] - pos[i - 1]);
    sort(v.begin(), v.end());
    long long l = 1, r = v[v.size() - 1], mid;
    while(l < r)
    {
        mid = (l + r) >> 1;
        //cout << mid << endl;
        if (check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    cout << l;
    return 0;
}
原文地址:https://www.cnblogs.com/lipoicyclic/p/14350382.html