2021百度之星初赛二(1001 -- 1003)

2021百度之星初赛二(1001 – 1003)

1001
题意:
给 a,b,每次 a,b会变为 a+b,a-b,问 k 次之后变成了哪两个数,对 998244353998244353 取模,多组数据。
题解:
首先观察前几次变化,
0 -------- a b 1 -------- a - b a + b
2 -------- 2a 2b 3 -------- 2a - 2b 2a + 2b
……
不难发现
对于k为偶数,答案是 a * pow(2,k/2) b * pow(2,k/2);
对于k为奇数,答案是 (a-b) * pow(2,(k-1)/2) (a+b) * pow(2,(k-1)/2);
ps:由于k可以达到1e9,因此使用快速幂。

#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <stack>
#include <cstring>
#include <map>
#define INF 0x3f3f3f3f
#define ll long long
const double pi = acos(-1.0);
using namespace std;
const ll mod = 998244353;

ll qpow(ll a, ll b) {
    ll ans = 1;
    while (b) {
        if (b & 1) {
            ans = (ans * a) % mod;
        }
        b >>= 1;
        a = (a * a) % mod;
    }
    return ans;
}
int main() {
    int t; cin >> t;
    while (t--) {
        ll a, b, c, d;
        cin >> a >> b;
        c = (a + b) % mod;
        d = (a - b + mod) % mod;//这里的mod一定要加,像我不加卡半小时
        ll k; cin >> k;
        ll s = k / 2;
        ll e = k % 2;
        ll pre = qpow(2, s);
        ll ans1, ans2;
        if (e & 1) {
            ans1 = (c * pre) % mod;
            ans2 = (d * pre) % mod;
        }
        else {
            ans1 = (a * pre) % mod;
            ans2 = (b * pre) % mod;
        }
        cout << ans1 << " " << ans2 << endl;
    }
}

1002
题意:
在这里插入图片描述
题解:
根据题意,可以发现a[n]的排列顺序对答案是不会产生影响的
比如 a={ 2, 3 },k = 1, 那么一种构造为b={1, 2},x=2;
现在改变a的顺序为{3,2},那么相应的b={2,1},x=2;
因此,不妨把a变为一个升序序列,为了使b的数字种类够多,可以在构造b时,往小处取值,并保证数字种类的不同。这样构造出来的b也是一个升序序列。
这里可以使用map,用于表示对于某种数字,当前可以使用的最小值。

#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#define INF 0x3f3f3f3f
#define ll long long
const double pi = acos(-1.0);
using namespace std;
const ll mod = 998244353;
ll a[100005]; map<ll, ll> pos;
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t; cin >> t;
    while (t--) {
        int n; cin >> n;
        ll k; cin >> k;
        pos.clear();
        for (int i = 0; i < n; i++) {
            cin >> a[i];
            pos[a[i]] = a[i] - k;//该数字初始可使用的最小值一定是 数字-k
        }
        ll ans = 0;
        sort(a, a + n);
        //a[0] = pos[a[0]];
        pos[a[0]]++;//第一位的最小值选定,可选最小值加1
        ans++;
        for (int i = 1; i < n; i++) {
            //如果当前数字的可选最小值比上个数字小,表明当前最小值不符合条件,必然和之前数字有重复
            //因此要进行更新
            if (pos[a[i]] < pos[a[i - 1]])pos[a[i]] = pos[a[i - 1]];
            //在没超过k的范围内,数字种类可增加,并更新可选最小值
            if (pos[a[i]] <= a[i] + k) {
                pos[a[i]]++;
                //a[i] = pos[a[i]] - 1;
                ans++; 
            }
            else {
                //a[i] = pos[a[i]] - 1;
            }
        }

        cout << ans << "\n";
    }
}

1003
题意:
给一张无向完全图,每条边有一个颜色,为黑色或者白色。你初始在点 s 上,你每次可以从当前的点经过一条边走到另一个点上,并将走过的边的颜色翻转。你想要把图中所有的边全都变为黑色,要求最小化走过的边的条数,求这个最小值,或者判断无解。
题解:
首先,题中所给的图是完全图,则必然是简单图,且每两点之间都有边相连。
要把所有白边变为黑边,白边要经过奇数次,黑边变为黑边,黑边要经过偶数次。为了使次数减少,那么原题可变为所有白边都经过1次并且借助最少的黑边的方案。
我们可以先把所有白边单独构图,作为一个白边子图。这样你会得到几个仅有白边的连通分量。为了使所有白边能经过,每两个连通分量间用黑边连接。由于是完全图,这样的黑边是必然存在的。
黑边要经过两次,因此加入到白边图中就等价于一对白色重边。
这样其实就构造好了一张连通的白边子图,这张子图的总边数就是我们要的答案。
但这个子图不一定是能达到要求的。子图所有边都是要不重复经过一次的,那就要用欧拉定理来判断。
这里说明一下,之前加黑边的操作是为了更好理解。判断是否为欧拉图/半图,初始的白边图就行。因为黑边只会给节点的度数加2,这不影响节点的奇偶性。
如果这不是欧拉图/半图,可以判为不可行。如果是欧拉图,那一定要判断起点是否为孤立节点,如果孤立,则必须与白边子图间用黑边连接。如果是欧拉半图,要是起点的度数不是奇数,则判为不可行。
图利用并查集储存。

#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <cstring>
#define INF 0x3f3f3f3f
#define ll long long
const double pi = acos(-1.0);
using namespace std;
const ll mod = 998244353;
int degree[1005];
//==========================================
int net1[100010];
int afind1(int i)
{
    if (net1[i] == i)return i;
    else return net1[i] = afind1(net1[i]);
}
void u1(int x, int y)
{
    if (afind1(x) != afind1(y))net1[afind1(x)] = afind1(y);
    else return;
}
//==============================================
int net2[100010];
int afind2(int i)
{
    if (net2[i] == i)return i;
    else return net2[i] = afind2(net2[i]);
}
void u2(int x, int y)
{
    if (afind2(x) != afind2(y))net2[afind2(x)] = afind2(y);
    else return;
}
//==============================================

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t; cin >> t;
    while (t--) {
        int n; cin >> n;
        int str; cin >> str;
        memset(degree, 0, sizeof degree);
        for (int i = 0; i <= n; i++) {
            net1[i] = net2[i] = i;
        }
        int whi = 0;
        for (int i = 2; i <= n; i++) {
            string a; cin >> a;
            for (int j = 0; j < a.length(); j++) {
                if (a[j] == '0') {
                    u1(i, j + 1);//构造白边子图。
                    whi++;//白边加1
                    degree[i]++; degree[j + 1]++;//对应节点度数加1
                }
                else {
                    u2(i, j + 1);//构造黑边子图,这个应该不需要,请忽略。
                }
            }
        }
        bool f = 1; int jie = 0;
        for (int i = 1; i <= n; i++) {
            if (degree[i] & 1)jie++;
        }
        if (!(jie == 0 || jie == 2))f = 0;//判断是否为欧拉图/半图
        if (f) {
            if (jie == 2) {
                if (!(degree[str] & 1))f = 0;//半图且起点度数不为奇数,不可行
            }
        }
        //cout << degree[1] << degree[2] << endl;
        if (f) {
            bool che[1005] = { 0 };
            for (int i = 1; i <= n; i++) {
                if (degree[i])che[afind1(i)] = 1;//孤立节点不判断为一个连通分量
            }
            int cnt = 0;
            for (int i = 1; i <= n; i++) {
                if (che[i])cnt++;//连通分量计数
            }
            if (degree[str] == 0)cnt++;//如果起点孤立,起点也作为一个连通分量
            //cout << whi << " :" << cnt << endl;
            if (whi)whi += 2 * (cnt - 1);//白边数加所需的黑边数的两倍

            cout << whi << "\n";
        }
        else {
            cout << -1 << "\n";
        }
    }

}

the end。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值