牛客周赛 Round 64(博弈论、思维、构造、LCA、换根DP)

牛客周赛 Round 64(博弈论、思维、构造、LCA、换根DP)

A. 小红的对错判断

根据题意判断即可。

#include<bits/stdc++.h>

using namespace std;

int main(){
    
    string s;
    cin >> s;
    
    string a = "YES", b = "yes";
    
    if(s.size() != 3) cout << "wrong answer" << endl;
    else{
        int flag = 0;
        for(int i = 0; i < 3; i++) flag += (a[i] == s[i] || b[i] == s[i]);
        if(flag == 3) cout << "accept" << endl;
        else cout << "wrong answer" << endl;
    }
    
    return 0;
}

B. 小红的幂表达

从大到小枚举底数,判断是否为整次幂。

#include<bits/stdc++.h>

using namespace std;

int main(){
    
    int n;
    cin >> n;
    
    cout << n << endl;
    for(int i = n; i >= 2; i--){
        int cnt = 0, x = n;
        while(1){
            if(x % i == 0) cnt++, x /= i;
            else break;
        }
        if(x == 1) cout << '=' << i << '^' << cnt << endl;
    }
    
    return 0;
}

C. 小红的前缀询问

维护每个元素出现的次数,边维护边统计其对答案的贡献。

#include<bits/stdc++.h>

using namespace std;

int main(){
    
    int n;
    cin >> n;
    map<int, int> v;
    long long res = 0;
    for(int i = 1; i <= n; i++){
        int x;
        cin >> x;
        res += v[x];
        v[x]++;
        cout << res << " ";
    }
    
    return 0;
}

D. 小红和小紫的博弈游戏(博弈论)

为了方便表示,把 2 * 2 的矩阵的四个元素定义为 1、2、3、4,如下图所示。
在这里插入图片描述

考虑一种情况:假设元素1 的值为 0。
这时,每次操作只能选择 (元素2,元素4) 或 (元素3,元素4),最多的操作次数:
X = m i n ( v a l u e ( 元素 2 ) + v a l u e ( 元素 3 ) , v a l u e ( 元素 4 ) ) X = min(value(元素2)+value(元素3),value(元素4)) X=min(value(元素2)+value(元素3)value(元素4))
即,要么元素2和元素3拿完,剩下元素4;要么元素4 拿完,剩下元素2和元素3。
下面两个例子对应两个 ‘要么’ :

例1:0 1 3 5
列2:0 6 8 3
分别对应四个元素的value

对于操作次数 X:

  1. 当 X 为奇数时,先手必胜。(例2,操作3次)
  2. 当 X 为偶数时,后手必胜。(例1,操作4次)

对于例1,给四个元素整体加 10,得到例3:

例3:10 11 13 15

这时,对于后手来说,肯定希望通过操作,使得==由’例3‘变为’例1‘==的情况。
是否可行呢?


显然可以,当先手选12时,后手选34;先手选13时,后手选24 …

每轮操作,后手都选先手没有操作的两个元素,即可实现使四个元素整体减一,直到出现==’例1’==,后手必胜。

综上,由特殊情况的后手必胜,得到了一般情况的后手必胜。


进一步思考,在上述情况的基础上,多一手操作,会怎么样?

显然,是先手必胜。

先手只需要第一步操作时,选择多出来的这一手操作,即可转化为 ‘先手’ 变 ’后手‘ 且出现 ’后手必胜的局面‘。


完。


code:

#include<bits/stdc++.h>

using namespace std;

int a[10];

void rotate(){    // 逆时针旋转90°,看图
    int x = a[1];
    a[1] = a[2];
    a[2] = a[3];
    a[3] = a[4];
    a[4] = x;
}

int main(){
    
    int ncase;
    cin >> ncase;
    
    while(ncase--){
        for(int i = 1; i <= 4; i++) cin >> a[i];
        int mn = a[1], mx = a[1];
        for(int i = 1; i <= 4; i++) mn = min(mn, a[i]);
        for(int i = 1; i <= 4; i++) a[i] -= mn;
        while(a[1] != 0) rotate();    // 让 0 在 1 的位置
        int cnt = min(a[4], a[2] + a[3]);
        if(cnt) cout << "kou" << endl;
        else cout << "yukari" << endl;
    }
    
    return 0;
}

E. 小红的字符串重排(思维、构造)

显然,出现最多的字符的个数 <= n / 2 时,有解。

一种排列思路:依次选择出现次数最多的字符与出现次数最少的字符换位置。

对于上述排列思路,会出现一个情况,如下:

a c c d d d e e e e

交换后,中间会剩下两个 d 还在原位,如何处理?

在剩下两个d时,此时交换操作的序列为:

a e
c e
c e
d e

取交换序列的前两次操作中的两个e与剩下的两个d 交换位置,结果为:

d d e e e e a c c d

证明这种方法一定可行:

  • 在交换序列中,序列的长度等于“出现最多的字符的个数”。故而,可用作上述操作的字符的个数 = “出现最多的字符的个数”
  • 出现次数最多的元素 <= n / 2,中间剩下的元素一定不是出现次数最多的字符。故而,中间剩下的元素的个数 <= “出现次数最多的元素”
  • 综上,中间剩下的元素的个数<= 序列的长度 = 可用作交换的元素个数
#include<bits/stdc++.h>

using namespace std;

vector<int> v[30];
pair<int, int> a[30];
queue<pair<int, int> > qu; // 交换序列

int main(){
    
    string s;
    cin >> s;
    int n = s.size();
    for(int i = 0; i < n; i++) v[s[i] - 'a'].push_back(i);
    
    for(int i = 0; i < 26; i++) a[i].first = v[i].size(), a[i].second = i;
    
    sort(a, a+26);
    
    if(a[25].first > n / 2) cout << -1 << endl;
    else{
        int i = 0, j = 25, x = 0, y = 0;
        while(i <= j){
            if(i != j){
                while(x < v[a[i].second].size() && y < v[a[j].second].size()){
                    qu.push({v[a[i].second][x], v[a[j].second][y]}); // 少的放在前 
                    x++;
                    y++;
                }
                if(x == v[a[i].second].size()) i++, x = 0;
                if(y == v[a[j].second].size()) j--, y = 0;
            }
            else{
                for(int z = max(x, y); z < a[i].first; z++){
                	pair<int, int> p = qu.front();
                	qu.pop();
                	qu.push(p);	
                	qu.push({p.first, v[a[i].second][z]});
				}
				i++;
            }
        }
        int flag = 1;
        while(!qu.empty()){
        	pair<int, int> p = qu.front();
        	qu.pop();
        	swap(s[p.first], s[p.second]);
        	if(s[p.first] == s[p.second]) flag = 0;
		}
		if(flag) cout << s << endl;
		else cout << -1 << endl;
    }
    
    return 0;
}

F&G. 小红的树上路径查询(LCA、换根DP)

结论1:在无向图中,对于一个任意一个点 x,到路径(u,v)的距离为: ( d i s t ( u , x ) + d i s t ( v , x ) − d i s t ( u , v ) ) / 2 (dist(u, x) + dist(v, x) - dist(u, v)) / 2 (dist(u,x)+dist(v,x)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值