2022CCPC 绵阳 2022 China Collegiate Programming Contest (CCPC) Mianyang Onsite 补题(6/13)

A 记忆化,博弈dp

C 签到,dfs思维

E 倒推,分块,multiset,图论

G 链表模拟

H 无聊构造

M 神仙栈模拟

 A. Ban or Pick, What's the Trick

Bobo has recently learned how to play Dota2. In Dota2 competitions, the mechanism of banning/picking heroes is introduced, modified and simplified as follows for the sake of the problem:

Suppose a game is played between two teams: Team A and Team B. Each team has a hero pool of nn heroes with positive utility scores a1,…,ana1,…,an and b1,…,bnb1,…,bn, respectively. Here we assume all heroes in two teams' hero pool are distinct.

The two teams then perform ban/pick operations alternately, with Team A going first. In one team's turn, it can either pick a hero for itself, or ban an unselected hero from the opponent's hero pool.

After 2n2n turns, all heroes are either picked or banned. Each team then needs to choose at most kk heroes from all heroes it picked to form a warband and the score for the warband is calculated as the sum of utility scores over all heroes in it.

Let sA,sBsA,sB be the score of the warband formed by Team A and Team B, respectively. Team A wants to maximize the value of sA−sBsA−sB while Team B wants to minimize it.

Bobo wants to know, what should be the final value of sA−sBsA−sB, if both teams act optimally? He's not really good at calculating this, so he turned to you for help.

贪心是很难证明正确性,考虑dp,而当前选择什么对后面完全是有影响的,所以成了赛时卡掉dp想法的关键。但本题并不是从前往后推,我们设置dp[i][j][k]为 包括i这局在内,A选了j,B选了k的收益,当前的dp值完全由后面的状态转移而来,写for循环dp的话,不是太好写,改写为记忆化,nown,nowa,nowb代表第几轮,A选了几个,B选了几个

nown为奇数的时候,A开始选,前nown-1的状态是,A选了nowa个,BAN了(nown/2)-nowa个,B选了nowb个,BAN了(nown/2)-nowb个。当前有两种选择,一个是A选择英雄,另一个便是ban,二者取max

nown为偶数的时候,B同理,但是要取min,选择的代价为负数

还有值得注意的便是,我们每次都操作最大的,显然是最优的,这需要提前排序

# include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
int dp[200000+10][12][12],a[100000+10],b[100000+10];
int n,k;
bool cmp(int x,int y)
{
    return x>y;
}
bool vis[2000000+10][12][12];

int dfs(int nown,int nowa,int nowb)
{
    if(nown>2*n)
        return 0;
    if(vis[nown][nowa][nowb])
        return dp[nown][nowa][nowb];
    vis[nown][nowa][nowb]=1;

    if(nown%2==1)
    {
        int tempn=nown/2+1;
        tempn-=nowb;//ban的
        tempn+=nowa;
        dp[nown][nowa][nowb]=dfs(nown+1,nowa,nowb);
        if(tempn<=n&&nowa<k)
            dp[nown][nowa][nowb]=max(dp[nown][nowa][nowb],dfs(nown+1,nowa+1,nowb)+a[tempn]);

    }
    else
    {
        int tempn=(nown)/2+1;
        tempn-=nowa;
        tempn+=nowb;
        dp[nown][nowa][nowb]=dfs(nown+1,nowa,nowb);
        if(tempn<=n&&nowb<k)
            dp[nown][nowa][nowb]=min(dp[nown][nowa][nowb],dfs(nown+1,nowa,nowb+1)-b[tempn]);
        }
    return dp[nown][nowa][nowb];
}
int main ()
{
    cin>>n>>k;
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
    }
    for(int i=1; i<=n; i++)
    {
        cin>>b[i];
    }
    sort(a+1,a+1+n,cmp);
    sort(b+1,b+1+n,cmp);

    cout<<dfs(1,0,0);

    return 0;
}

C. Catch You Catch Me 

Bobo accidentally dropped into the secret garden when he was listening to Song from a secret garden. Guess what did he see? Butterflies!

Yes, it is.

The structure of the secret garden can be modeled as an undirected tree of nn nodes labeled from 11 to nn, where the node labeled 11 is the exit of the secret garden. There is initially a butterfly at each node labeled from 22 to nn. Bobo noticed that, at each minute, every butterfly would move through an edge towards the direction of the node labeled 11. When a butterfly flies to the node labeled 11, it leaves the secret garden and disappears forever.

Bobo wants to catch all the butterflies before they fly to the exit and disappear. Bobo is initially not at any node of the tree. What he can do is to choose any node of the tree at some minute (including minute 00, where all butterflies are at their respective nodes) and catch all butterflies in that node (Bobo cannot catch butterflies at node 11 as butterflies disappear instantly when they get there). This counts as an operation. Bobo acts really fast, and the time it takes for him to operate can be neglected. Bobo wonders, what is the minimum number of times he has to operate to catch all butterflies?

赛时脑子抽风,什么也没想以为是度数为1的点就够了--

贪心的想,我们尽可能的把全部蝴蝶汇聚的一个点,当然不可能是1这个点,而是1的儿子们。那么对于各个儿子,其实是互不影响,并列的,答案要累加的。相同深度的节点,最终会同时到达儿子,所以本质就是儿子子树(含儿子本身)的最大深度-1(1节点深度为1)

dfs即可 

#include <bits/stdc++.h>
using namespace std;
vector<int> v[100000 + 10];
int dep[100000 + 10];
set<int> s[100000 + 10];
int ans;
int dfs(int now, int pre)
{
    dep[now] = dep[pre] + 1;
    int res = dep[now];
    for (auto it : v[now])
    {
        if (it == pre)
            continue;
        int tmp = dfs(it, now);
        res = max(res, tmp);
        if (now == 1)
        {
            ans += tmp - 1;
        }
    }
    return res;
}
int main()
{

    int n;
    cin >> n;

    for (int i = 1; i < n; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        v[x].push_back(y);
        v[y].push_back(x);
    }

    dfs(1, 0);
    cout << ans << "\n";
    return 0;
}

Problem - E - Codeforces

 E. Hammer to Fall

Boboland is a beautiful country with nn cities and mm two-way roads, ruled by King Bobo. A two-way road (u,v,w)(u,v,w) connects city uu and city vv with distance ww. It is guaranteed that for each city in Boboland, at least one road connects to it.

There were aiai residents in the ii-th city, living a happy and peaceful life until the day came when it began to fall hammers! Specifically, every day hammers will start to fall on exactly one city of Boboland, causing all residents in that city to die.

As the king of Boboland, Bobo needs to deal with this situation. But unfortunately, he doesn't know why this disaster would happen, and he can't figure out a way so that the hammer would eventually stop falling. Despite this, he figured out the following "dynamic zero-casualty policy": it is OK as long as when the hammer falls on some city someday, all residents in that city have already been transferred to some other city so that no death is incurred.

After talking with the prophet in Boboland, Bobo has gained the following information: in the upcoming qq days, hammers will fall on cities b1,b2,...,bqb1,b2,...,bq in order. At any time, Bobo can arrange to transfer any single resident at some city uu to some adjacent city vv with cost ww if a road (u,v,w)(u,v,w) exists. Multiple residents can be transferred simultaneously. Also, any resident can be transferred multiple times.

Bobo wants to ensure no resident dies after the qq days, using as little cost as possible. But, as always, a king never does the counting by himself, so you have to calculate the minimum cost to achieve the goal.

画图其实更好理解,赛时思维集中在这是图论题,要跑最短路上,没想到是个dp,还是倒推dp. dp[i]代表将i这个点全部转移至外面的最小花费,倒叙遍历每次灾难,如下图,两个绿点更新完将居民转移的花费后,红点继承二者的最小花费+边权,这样红点就包含了将居民运出红点并且接着在绿点灾难来临时运出绿点的花费。这里的花费,指的是边权,也就是运输一个人的花费,最后还需要*a[i]

直接dp之后发现会T,瓶颈在于q*cnt,  cnt代表每个节点的度数,所以考虑对度数进行分块。

分块的最为重要的两点是,第一设置分块边界,第二便是扬长避短,利用边界内个数多,但执行次数少,边界外执行次数多,但个数少的特点,进行构建。 在本题中,个数就是点个数,执行次数就是其遍历节点的个数。我们考虑扬长避短。

设这一分界值是  val  ,我们遍历询问,如果这个点的度数<=val,考虑暴力dp,复杂度 q* val

如果大于val,不能直接dp,考虑其数量为 总度数/ val, 总度数2*m ,个数不会超过2*m/val, 我们每次得到一个新的dp值,就暴力修改连接他的全部度数>val的节点, 如果val值设置合理,其遍历复杂度与每次暴力遍历<=val的点同阶。 用multiset实现,每次修改复杂度log2(n)

总复杂度为(q*val + q * 2*m/val * log2(n)),q(val +2*m*log2(n) /val)

val +2*m*log2(n) /val 在 val=  sqrt( 2*m*log2(n) )时,复杂度最优

# include<bits/stdc++.h>
using namespace std;
typedef long long int ll;

vector<pair<int,ll>>v[100000+10];
ll a[100000+10];
ll du[100000+10];
ll x[100000+10];
vector<pair<int,ll>>vv[100000+10];
ll dp[100000+10];
struct node
{
    int id;
    ll dp;
    ll w;
    friend bool operator<(node x, node y)
    {
        return x.dp+x.w< y.dp+y.w;
    }
};
multiset<node>s[100000+10];
# define mod 998244353
int main ()
{

    int n,m,q;
    cin>>n>>m>>q;
    ll len=sqrt(1000);
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
    }
    for(int i=1; i<=m; i++)
    {
        int x,y;
        ll z;
        scanf("%d%d%lld",&x,&y,&z);
        v[x].push_back(make_pair(y,z));
        v[y].push_back(make_pair(x,z));
        du[x]++;
        du[y]++;
    }

    for(int i=1; i<=n; i++)
    {
        for(auto it:v[i])
        {
            if(du[it.first]>len)
            {
                vv[i].push_back(it);
            }
        }

        if(du[i]>len)
        {

            for(auto it:v[i])
            {
                s[i].insert(node{it.first,0,it.second});
            }
        }
    }
    for(int i=1; i<=q; i++)
    {
        scanf("%d",&x[i]);
    }


    for(int i=q; i>=1; i--)
    {
        ll predp=dp[x[i]];

        if(du[x[i]]<=len)
        {
            ll minn=1e18;
            for(auto it:v[x[i]])
            {
                minn=min(minn,it.second+dp[it.first]);
            }
            dp[x[i]]=minn;
        }
        else
        {
            auto it=*s[x[i]].begin();
            dp[x[i]]=it.dp+it.w;

        }
        for(auto it:vv[x[i]])
        {
            struct node now;
            now.id=x[i];
            now.dp=dp[x[i]];
            now.w=it.second;

            auto pos=s[it.first].lower_bound({x[i],predp,now.w});
            s[it.first].erase(pos);
            s[it.first].insert(now);
        }
    }

    ll ans=0;

    for(int i=1;i<=n;i++)
    {
        ans+=(a[i]*dp[i])%mod;
        ans%=mod;
    }
    cout<<ans;

    return 0;
}

Life is Hard and Undecidable, but...

感觉没有任何意义的题,不考察任何代码能力与思维,单纯就是一条对角线 

 M. Rock-Paper-Scissors Pyramid

The rock-paper-scissors is a well-known hand game that originated in China, usually played between two people, in which each player simultaneously forms one of three shapes with an outstretched hand. These shapes are "rock" (a closed fist), "paper" (a flat hand), and "scissors" (a fist with the index finger and middle finger extended, forming a V). "Scissors" is identical to the two-fingered V sign (also indicating "victory" or "peace") except that it is pointed horizontally instead of being held upright in the air. The rule is simple. Paper beats rock. Rock beats scissors. Scissors beats paper.

Example of Rock Paper Scissors. Source: Wikipedia

We use the uppercase letters RR, PP and SS to represent rock, paper and scissors, respectively. Given an initial string ss containing RR, PP and SS of length nn. Bobo designs a "rock-paper-scissors" pyramid of height and base length nn as follows: Place the initial string ss placed at the bottom nn blocks in order, then the pyramid evolves by the following rule:

  • If the two blocks immediately below a block have the same shape (namely, both are RR, PP, or SS), we will place the same shape on the block.
  • If the two blocks immediately below a block have different shapes, we will place the winning shape on the block.

Refer to the pictures in the notes section for a clearer illustration.

Bobo wants to know, following this rule, what is the shape at the top of the pyramid?

感觉是一道很好的题目,联想到类似于单调栈的数据结构,如果要放入的比栈顶小,那么放进去,否则就pop一堆,再放进去,最后栈底就是答案。样例二的图片很直观的表现了这一点,拳头压着剪刀形成一堵墙,就是我们pop的操作。再有就是,如果当前比栈顶小,我们为什么要放进去,而不是不放,样例1解释了这一点,如果布不放进去,拳头会把剪刀pk掉,而实际上拳头是被布给隔离住了,所以我们还要放进去。

#include <bits/stdc++.h>
using namespace std;
bool ck(char a, char b)
{
    if (a == 'S' && b == 'P')
        return 1;
    if (a == 'P' && b == 'R')
        return 1;
    if (a == 'R' && b == 'S')
        return 1;
    return 0;
}
int main()
{

    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;

    while (t--)
    {
        string str;
        cin >> str;
        stack<char> st;
        for (int i = 0; i < str.size(); i++)
        {
            while (st.size() && !ck(st.top(), str[i]))
            {
                st.pop();
            }
            st.push(str[i]);
            // cout << st.size() << "\n";
        }
        while (st.size() > 1)
            st.pop();
        cout << st.top() << "\n";
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qinsanma and Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值