4.6 模拟赛小记

T1 猴子选大王

对于第一个子任务,看一下 luogu P8671 约瑟夫环,用 f 存当前所选个数下的答案下标,易推得公式 f[i] = (f[i - 1] + m) % i。(后记:当时看的一篇该题题解理解了,后来题解撤了??然后还是不太懂,可以看一下 oiwiki (虽然解释的依旧很水),以及知乎有一个证明。总之这个可以当做函数自己证明一下。感觉该式子的思路就是根据上一次的幸存者,对于这一次把新加入的人加入环中,然后找这次幸存者,在确定其在环中的位置。证明的话就注意和前面式子在环上的关系,以及转为环后的位置)。

怎么推得?正难则反,不妨倒推环中剩下的人数,从环中有一人、二人...一直到最后的答案,当环中有 n 个人时赢。这样就比较好推了(我们把编号整体减 1,这样方便取模):环中只剩一人:f[1] = 0,f[2] = (f[1] + m) % 2,f[3] = (f[2] + m) % 3....f[i] = (f[i - 1] + m) % i。可以理解为:f[i - 1] 表示前面那个没死啦,+m 即来更新一下在环中新位置,%i 来确定在环上的位置。

对于第二个子任务,当 m = 1 时,不难得到最后一个即为答案。

对于第三个子任务,可以模拟进行规律寻找。答案即找到这个数二进制下最高位 1,该数与这个 2 的幂之差乘二加一。太抽象了,具体见代码,还是建议自己手动模拟。


#include<bits/stdc++.h>
#define intt long long
using namespace std;
const intt N = 1e8 + 10;
intt n, m;
intt vis[N];
void did1()
{
    intt sum  = 0;
    for(int i = 2; i <= n; i ++ ) sum = (sum + m) % i;
    cout << sum + 1;
}
void did2() {cout << n;}
intt bpow(intt a, intt b)
{
    intt ans = 1;
    while(b)
    {
        if(b % 2) ans = ans * a;
        a = a * a;
        b /= 2;
    }
    return ans;
}
void did3()
{
    intt i = 0, nn = n;
    while(nn)
    {
        nn >>= 1;
        i ++;
    }
    i --;
    printf("%lld", (n - bpow(2, i)) * 2 + 1);
}
signed main()
{
    scanf("%lld%lld", &n, &m);
    if(n <= 10000000)
    {
        did1();
        return 0; 
    }
    if(m == 1)
    {
        did2();
        return 0;
    }
    if(m == 2)
    {
        did3();
        return 0;
    }
}

T2 好人与坏人

还是和约瑟夫问题有关,在早期我甚至做过,居然不会写了,属实难绷。

模拟每次数的数,然后暴力算出每次死的人,看看好人死没死,然后如果坏人都死光光了就赢了。当时的思路过于暴力...每次算谁死了没必要暴力计算,直接模一下就好。

#include<bits/stdc++.h>
using namespace std;
int k, n, m;
int main()
{
    scanf("%d", &k);
    n = 2 * k;
    while(1)
    {
        int a = 0;
        m ++;
        for(int i = 0; i < k; i ++ )
        {
            a = (a + m - 1) % (n - i);
            if(a < k) break;
            if(i == k - 1)
            {
                printf("%d", m);
                return 0;
            }
        }
    }
}


T3 蚂蚁上树 洛谷:P2907 [USACO08OPEN]Roads Around The Farm S

小红题,当时写挂是因为读错题力。需要反思,我记得当天这个红题调了一节课,羞耻捏。错误原因是读错题理解错题意了。感觉因为读错题而错的问题已经很多次了,感觉需要更加认真。。。(当时以为两窝蚂蚁,分为两窝之后一窝就可以不要了???

然后就是递归,每次的结果为分为两窝的结果,一直分,直到分不了为止。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, k;
int ans(int q)
{
    if((q - k) % 2 || q < 2 + k) return 1;
    else return ans((q - k) / 2) + ans((q - k) / 2 + k); 
}
signed main()
{
    scanf("%lld%lld", &n, &k);
    printf("%lld", ans(n));
    return 0;
} 

T4 能量石
至今不会。听说正解是斜率优化,似乎没人做。


T5 最短周游
至今不会。正解是分治 + st表。


后记废话:当天模拟赛时很智障的。尤其是在知道自己做的是红加橙的大水题后。也发现很多基础问题没有落实好,而且我本身,也确实,不喜欢写递归。从自身找原因的话确实有很多问题好吧。就这样吧。慢慢积累慢慢进步,落实基础。别着急,别和别人比。

posted @ 2023-04-06 23:18  Moyyer_suiy  阅读(27)  评论(1编辑  收藏  举报