最后一轮“大工之星”部分题解

为AI而来

改编自南京某大学算法域。

若将魔术箱看作一个函数,本题的条件即可转化为:

已知函数 f(x) ,f(f(x))=3 , 且 f(x) 为单调递增函数,且 f(x)>x ,求  f(n)  。

问题关键在于求出 f(1) : 由题意可得,f(1)>1 ,由 f(f(1))=3  可知  f(1)<3 ,因此  f(1)=2 。

此时可以求出 f(2)=3 。之后可通过前面已求得式子得出 f(3)=6 ,f(4)=7 ,f(5)=8 ,f(6)=9 ,f(7)=12f(8)=15……

通过观察可以发现,f(2)=3 ,f(3)=6 ,f(6)=9 ,f(9)=18 ,f(18)=27 ,f(27)=54……满足 f(3*i)=3*f(i)(实际上这个性质对于任意数皆满足(lll¬ω¬)),上述每两项之中的相邻函数值呈现出“相差为1”“相差为3”的情况交替出现的形式。故仅需用数组记录这几个特殊函数值后讨论其为相差的哪种形式即可。

下为参考代码:

#include <bits/stdc++.h>
using namespace std;
int m;
int main()
{
    cin >> m;
    for (int i = 0; i < m; i++)
    {
        long long x;
        cin >> x;
        long long r = 2;
        while (r < x)
            r *= 3;
        long long l = r / 2;
        if (l <= x)
            cout << r + x - l << endl;
        else
            cout << l + (x - r / 3) * 3 << endl;
    }
    return 0;
}

瑞平b50

本题改编自力扣198.打家劫舍,在原题的基础上增加了一维。

dp[i][j] 的意思为“当遍历第 j 首时,选取不大于 i 首乐曲时 b50's \ rating 的最大值”。

若不选取第 j 首歌,则继承第 j-1 ,j-2 首的状态;

若选取第 j 首歌,则无法选取第 j-1 ,j-2 首歌,则此时的总 rating 为继承自第 j-3 首歌再加上第 j 首歌的 rating 。

因此转移方程为:

dp[i][j]=max \{ dp[i][j-2] \ ,dp[i][j-1] \ ,dp[i-1][j-3]+a[j] \}

由于 dp[i][j-1] 时已考虑 dp[i][j-2] 的情况,故转移方程可改为:

dp[i][j]=max \{dp[i][j-1] \ ,dp[i-1][j-3]+a[j] \}

下面处理边界情况:

当 i=1 时,dp[1][j] = max\{dp[1][j - 1], a[j]\}(即初始化为 a[1] 到 a[j] 中的最大值);

j \le 3 时,dp[i][j] = dp[i - 1][j](前三首乐曲一共只能选一首)

下为参考代码:

#include <bits/stdc++.h>
using namespace std;
int n;
long long a[100005], dp[51][100005];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= 50; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (i == 1)
                dp[i][j] = max(dp[i][j - 1], a[j]);
            else if (j <= 3)
                dp[i][j] = dp[i - 1][j];
            else
                dp[i][j] = max(dp[i][j - 1], dp[i - 1][j - 3] + a[j]);
        }
    }
    cout << dp[50][n] << endl;
    return 0;
}

如难

本题改编自力扣1863. 找出所有子集的异或总和再求和

EZ版本:暴力枚举一遍某个数是否出现即可通过(dfs或位运算遍历子集):

下为参考代码:

#include <bits/stdc++.h>
using namespace std;
const int mod = 114514;
string s;
int a[100005], ans, cnt, sum;
int main()
{
    cin >> s;
    int b = 1, c, d;
    c = d = b;
    for (int i = 0; i < s.size(); i++)
        a[i] = s[i] - 48;
    for (int i = 0; i < (1 << s.size()); i++)
    {
        ans = 0;
        for (int j = 0; j < s.size(); j++)
            if (i & (1 << j))
                ans ^= a[j];
        sum += ans % mod;
    }
    cout << (sum + 113618) % mod;
    return 0;
}

HD版本:不难发现:

1.数组中每个数皆在 0 ~ 9 中;

2.对于任意一个子集而言,由于异或满足自反性,任何一个出现偶数次的数,对答案没有影响(即删去后该子集异或总和不发生变化),只有出现奇数次才会对答案有影响;

3.对于任意一个子集而言,由于异或满足交换律,仅需统计一个子集中各数字出现的次数。

因此,可用 桶数组 存放该日期数字中 0 ~ 9 每个数字出现的次数,遍历每个数出现 奇数次 抑或 偶数次 。由于每个子集的异或总和只与 奇数次 出现的数相关,故只需统计遍历到的 奇偶情况相同 的子集的数量,乘以这种情况下的异或总和即可。

在本题中,由于元素 相同 的 不同 子集应 多次 计数。故在某个子集中出现 2n+1 次的数字选取奇数次的情况共有:

C_{2n+1}^{1}+C_{2n+1}^{3}+...+C_{2n+1}^{2n-1}+C_{2n+1}^{2n+1}=2^{2n} (组合数公式)

对于出现 2n 次选取情况类似,为 2^{2n-1} 。

此题同时也存在两个小细节:

1. 0 虽然对于每个子集的异或总和不造成影响,但是否选取 0 对于子集数量存在影响;

2.有的日期数字中可能并不包含 0 ~ 9 中每个数字,若直接冒然认为共有 2^{10} 情况则会重复计数。

(在测试时发现输出经常相同,但研究了半天也没总结出来,应该会有更好的做法,希望大佬不吝赐教QAQ)

下为参考代码:

#include <bits/stdc++.h>
using namespace std;
const long long mod = 114514;
string s;
long long a[10], exist[10], c[1005], cnt, ans, sum, numsize;
void ConbinationSum(int maxn)
{
    c[0] = 1;
    for (int i = 1; i <= maxn; i++)
        c[i] = c[i - 1] * 2 % mod;
}
int main()
{
    cin >> s;
    ConbinationSum(s.size());
    for (int i = 0; i < s.size(); i++)
        a[s[i] - 48]++;
    for (int i = 0; i < 10; i++)
        if (a[i])
        {
            exist[numsize] = i;
            numsize++;
        }
    for (int i = 1; i < (1 << numsize); i++)
    {
        ans = 0, cnt = 1;
        for (int j = 0; j < numsize; j++)
        {
            if (i & (1 << j))
                ans ^= (exist[j]);
            cnt = cnt * c[a[exist[j]]] / 2 % mod;
        }
        sum = (sum + ans * cnt % mod) % mod;
    }
    cout << (sum + 113618) % mod;
    return 0;
}

*最后十分钟时此题被a,思路看起来比我的简单得多(还是我太菜了嘛TAT),故一起放在这里:

#include <stdio.h>

//为什么不让我过 为什么不让我过 为什么不让我过 为什么不让我过 为什么不让我过 为什么不让我过 为什么不让我过 为什么不让我过 
//我急了我急了我急了我急了我急了我急了我急了我急了我急了我急了我急了我急了我急了我急了我急了我急了我急了我急了
//好想做末影人的狗啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 



int main() 
{
    long long int m,i,j,k,a[1200],sum,tem;
	tem=getchar();
	for(i=0;tem>='0'&&tem<='9';i++)
	{
		a[i]=tem-'0';
		tem=getchar();
		
	}
	m=i;
	sum=0;
	for(i=0;i<m;i++)
	{
		sum|=a[i];
	}
	for(i=0;i<m-1;i++)
	{
		sum=sum*2;
		if(sum>114514)
		{
			sum=sum+113618;
			sum=sum%114514;
			sum=sum-113618;
			if(sum<0)
				sum=sum+114514;
		}
	}
	sum=sum+113618;
	sum=sum%114514;
	printf("%lld",sum);
    return 0;
}

事实证明,只要够逆天,没有做不出来的题目。(

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值