首页 > 其他分享 >[Codeforces Round 960 (Div. 2)]A-E

[Codeforces Round 960 (Div. 2)]A-E

时间:2024-07-21 21:07:29浏览次数:10  
标签:960 71 int void Codeforces 数组 操作 Div mx

Codeforces Round 960 (Div. 2)A-E

A题意:公平博弈。给定一个数组n个数,每个数只能用一次。给一个\(mx\)。每次轮到自己操作的时候就选一个数组里的数,满足\(a[i]>=mx\),然后令\(mx=a[i]\).双方轮流做直到一方无法操作,则另一方取胜。

Sol:赛时1min猜了个错解,只看最大值,只看最大值的出现次数,回来发现wa了。如果最大值出现偶数次,但次大值出现奇数次,先手也能win。递归考虑这个过程我们发现只需要统计所有数出现次数的奇偶性。只要出现一个奇数先手就能win,策略是从最大的出现奇数次开的数开始拿。

void solve(){
       cin>>n;
       map<int,int>mp;
       for(int i=1;i<=n;i++)cin>>a[i];
       for(int i=1;i<=n;i++)mp[a[i]]++;
       bool flag=0;
       for(auto [x,y]:mp)if(y%2==1)flag=1;
       if(flag)cout<<"YES"<<endl;
       else cout<<"NO"<<endl;
}

B题意:给定数组长度和两个位置\(x,y(y<x)\),要求你构造只含-1和1的数组满足:x是最小的位置满足前缀和达到前缀和最大值,y是最大的位置满足后缀和数组达到后缀和最大值。

Sol:关键的突破点在于考虑到如果x这个位置作为最大值,起码x这个位置需要是1,反证显然。则y也同理。再考虑两边应该都是不能突破最大值的也就是需要-1,1,-1,1这样的。那么不难想到为了让x,y分别做到前缀最大和后缀最大,中间全放1即可。

void solve(){
	int x,y;
    cin>>n>>x>>y;
    for(int i=y;i<=x;i++)a[i]=1;
    for(int i=y-1;i>=1;i--){
    	if((y-i)%2==1)a[i]=-1;
    	else a[i]=1;
    }
   for(int i=x+1;i<=n;i++){
   	if((i-x)%2==1)a[i]=-1;
   	else a[i]=1;
   }
   for(int i=1;i<=n;i++)cout<<a[i]<<" ";
   cout<<endl;
    
}

C题意:每次对数组进行MAD操作,数组全为0终止。求每次操作后数组和的总和。MAD:新数组第i个数是上一轮数组的1-i的前缀中出现次数至少两次以上的数的最大值。

Sol:需要手动模拟几个样例,发现一轮操作后数组变成单调递增,但注意到这时候数组中只出现一次的数在下一轮会消失,而出现两次以上的数会存在较长时间。于是考虑再做一轮,我们发现当前数组中都是递增且长度大于2连续段.后面的操作会发现是右移。贡献可以\(O(1)\)计算。

void solve()
{
    cin >> n;
    vector<int> b(n + 1, 0);
    map<int, int> mp;
    int sum = 0;
    for (int i = 1; i <= n; i++)
        cin >> a[i], sum += a[i];
    int mx = 0;
    for (int i = 1; i <= n; i++)
    {
        mp[a[i]]++;
        if (mp[a[i]] >= 2)
        {
            mx = max(a[i], mx);
            b[i] = mx;
        }
        else
            b[i] = mx;
        sum += b[i];
    }
    //  for(int i=1;i<=n;i++)cerr<<b[i]<<" ";
    //  cerr<<endl;
    for (int i = 1; i <= n; i++)
        mp[a[i]]--;
    mx = 0;
    for (int i = 1; i <= n; i++)
    {
        mp[b[i]]++;
        if (mp[b[i]] >= 2)
        {
            mx = max(b[i], mx);
            b[i] = mx;
        }
        else
            b[i] = mx;
    }
    //  for(int i=1;i<=n;i++)cerr<<b[i]<<" ";
    // cerr<<endl;
    for (int i = 1; i <= n; i++)
        sum += b[i] * (n - i + 1);
    cout << sum << endl;
}

D题意:给出\(n*n\)方格,对于第\(i\)行前\(a[i]\)列是黑,剩余是白色。闲杂其需要你全部染白,问最少操作次数?有两种操作:

  • 将一个 $2×2 $子网格染成白色;
  • 选择将某一行染白。

Sol:样例非常贴心给了提示,想到了状态机dp,但是没有关键贪心的dp很大概率是不对的。本题我注意到的是当一行出现至少五个黑的时候一定是操作二更优。

  • 性质:但还需要注意到的是对于当前行有四个黑格且上一行没影响的时候一定不会选择两次操作1,因为即使能直接把下一行染完也不优于直接两次操作2染色两行。影响思考的样例明明有两行4个的用的操作1,但值得注意的那个是三行互相交接的。

有了这个性质我们得出如下贪心策略:

  • 对于1-2的我们策略1,有利于下一行。

  • 对于<=4的我们看当前前后哪段被染色了,如果有一段被染,那另一段用OP1。会发现一定不会出现前后两段都被提前染色了,因为由于上面的性质这是一定不优的。

  • 对于>=5当然是选择OP2

void solve()
{
    cin >> n;
    int ans = 0;
    bool b1 = 0, b2 = 0;
    //b1表示前面是不是已经为当前第1,2块染过白色了
    //b2表示前面是不是已经为当前第3,4块染过白色了
    // 对于当前还剩两块的时候才用方块策略
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= n; i++)
    {
        if ((b1 == 0) && (b2 == 0))
        {
            if (a[i] == 0)
                continue;
            else if (a[i] <= 2)
            {
                ans++;
                b1 = 1;
            }
            else
                ans++;
        }
        else if (b1 == 1)
        {
            b1 = 0;
            if (a[i] <= 2)
                continue;
            if (a[i] <= 4)
            {
                b2 = 1;
                ans++;
            }
            else
                ans++;
        }
        else if (b2 == 1)
        {
            b2 = 0;
            if (a[i] == 0)
                continue;
            if (a[i] <= 4)
            {
                b1 = 1;
                ans++;
            }
            else
                ans++;
        }
    }
    cout << ans << endl;
}

E:给定一棵树,根为1.现在要求你在160次查询内获得当前老鼠的位置。每查询失败一次,老鼠会跳到它当前位置的父节点。跳到1后不再变动。

Sol:考虑对于树高根号分治,目标是把老鼠逼到一条链上,最后二分找到位置。

  • 首先处理树高<=71的,找一个叶子不停无效询问使得老鼠不停的向上跳,把老鼠逼到根节点,然后后面二分统一处理。
  • 对于深度大于71的,从根节点开始递归询问。对于一个节点的儿子,只有深度大于71才被询问,小于70的一定已经在链上了,直接跳过即可。
    询问为真或者只有一个儿子的时候就直接递归进去,为假就下一个儿子(这样就就排除了71个点),因此这样的询问最多71次。
  • 最后二分18次足够完成,注意每次查询失败的时候。左右边界都要向根偏移一个位置,防止边界问题把老鼠放走了。
vector<int> e[N];
const int B = 71;
int query(int x)
{
    cout << "? " << x << endl;
    // cout.flush();
    int res;
    cin >> res;
    return res;
}
void solve()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
        e[i].clear();
    for (int i = 1; i <= n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    vector<int> p(n + 1, -1), h(n + 1);
    auto dfs1 = [&](auto self, int u, int fa) -> void
    {
        for (auto v : e[u])
        {

            if (v == fa)
                continue;
            p[v] = u;
            self(self, v, u);
            h[u] = max(h[u], h[v] + 1);
        }
    };

    dfs1(dfs1, 1, 0);
    int tmp = find(h.begin() + 1, h.end(), 0) - h.begin();
    // cerr<<tmp<<endl;
    for (int i = 1; i <= B; i++)
    {
        if (query(tmp) == 1)
        {
            cout << "! " << tmp << endl;
            return;
        }
    }
    auto dfs2 = [&](auto self, int u) -> int
    {
        vector<int> a;
        for (auto v : e[u])
        {
            if (v == p[u] || h[v] < B)
                continue;
            a.push_back(v);
        }
        if (a.empty())
            return u; // 走到老鼠所在链的叶子了
        for (auto x : a)
        {
            // 下面这个判断很重要,考虑到如果是一条链每次的分叉的数量大于70,如果只有一个点显然不需要判断这是一个重要的优化
            //如果有分叉,保证下面有70个点,则最多会查询71次
            if (x == a.back() || query(x) == 1)
            {
                int res = self(self, x);
                return res;
            }
        }
        assert(false);
    };
    int ed = dfs2(dfs2, 1);
    vector<int> a;
    for (int i = ed; i != -1; i = p[i])
        a.push_back(i);
    reverse(alls(a));
    int l = 0, r = a.size() - 1;
    while (l < r)
    {
        int mid = (l + r + 1) >> 1;
        if (query(a[mid]))
            l = mid;
        else
        {
            // 查询失败,答案区间需要整体偏移1
            r = mid - 1;
            l = max(0, l - 1);
            r = max(0, r - 1);
        }
    }
    cout << "! " << a[l] << endl;
}

标签:960,71,int,void,Codeforces,数组,操作,Div,mx
From: https://www.cnblogs.com/mathiter/p/18314954

相关文章

  • Codeforces Round 958 (Div. 2)
    Preface周末补题计划的最后一场,这场由于是最古早的所以有些题题意都记不太清了赛时经典发病,前四题一题WA一发,然后把时间打没了E题经典没写完(中间还抽空写了个假做法),邮电部诗人了属于是A.SplittheMultiset刚开始还感觉无从下手,写了个记搜交上去还WA了,后面发现每次分......
  • Codeforces Round 959 sponsored by NEAR (Div. 1 + Div. 2)
    Preface这场比赛的时候CF一直抽风,快10min的时候才登上去,然后中间交题也一直在人机验证50min左右过了前5题后,F没想到生成树当场趋势了,赛后发现原来是原题大战可海星A.DiverseGame将每个数循环移位变为下一个数即可,注意特判\(n=m=1\)的情形#include<cstdio>#incl......
  • Array Craft(Round 960)
    #include<bits/stdc++.h>#defineendl'\n'usingll=longlong;typedefunsignedlonglongull;usingnamespacestd;voidGordenGhost();signedmain(){#ifdefGordenfreopen("in.txt","rt",stdin);freopen......
  • Mad MAD Sum(Round 960)
    #include<bits/stdc++.h>#defineintll#defineendl'\n'usingll=longlong;typedefunsignedlonglongull;usingnamespacestd;voidGordenGhost();signedmain(){#ifdefGordenfreopen("in.txt","rt",stdin......
  • Codeforces Round 958 (Div. 2)
    A.SplittheMultisetForexample, {2,2,4} isamultiset.Youhaveamultiset ......
  • Codeforces Round 960 (Div. 2)(A - D)
    CodeforcesRound960(Div.2)(A-D)A-SubmissionBait解题思路:假设直接选最大数,如果最大数有奇数个,\(Alice\)必胜,反之必败。根据这个思路,从大到小看数字,找到第一个出现奇数次的数,从它开始选,就能保证每次\(Alice\)选的时候还剩奇数个选项。代码:#include<bits/stdc++.......
  • Codeforces Round 960 (Div. 2)
    Preface周日开始补之前欠下的CF博客,这周一共有三场就按照从后往前的顺序补吧这场经典前期唐完了,前4题上来每题WA一发也是神人了,最后做到E的时候只有50min了结果还把题看错了以为树的形态都是不确定的,怀疑人生了好久感觉这个题完全没法做,后面看了眼样例才发现树的形态......
  • Codeforces Round 960(Div.2)
    CodeforcesRound960(Div.2)A从大到小去判断数字个数的奇偶性,只要出现过奇数,先手必赢,如果不出现奇数,后手必赢#include<iostream>#include<queue>#include<map>#include<set>usingnamespacestd;constintN=55;inta[N];voidsolve(){ intn; cin>>n; ma......
  • 题解:Codeforces Round 960 (Div. 2) D
    D.GridPuzzletimelimitpertest:2secondsmemorylimitpertest:256megabytesinput:standardinputoutput:standardoutputYouaregivenanarray\(a\)ofsize\(n\).Thereisan\(n\timesn\)grid.Inthe\(i\)-throw,thefirst\(a_i\)......
  • 题解:Codeforces Round 960 (Div. 2) C
    C.MadMADSumtimelimitpertest:2secondsmemorylimitpertest:256megabytesinput:standardinputoutput:standardoutputWedefinethe\(\operatorname{MAD}\)(MaximumAppearingDuplicate)inanarrayasthelargestnumberthatappearsatleast......