Codeforces Round #843 (Div. 2)

C. Interesting Sequence(二进制)

题目大意
给定两个大于等于0的数\(n,\ x\),求满足\(n\&(n+1)\&(n+2)\cdots m=x\)的最小\(m\),若不存在输出-1。

解题思路
首先若\(n<x\)肯定无解。
\(l=n,\ r=5e18\),若解存在那么它必定处在\([l,r]\)中。
一次考虑二进制中\(n,\ x\)中的每一位,

  • \(n[i]==0\ \&\&\ x[i]==1\),无解(若\(n<x\)必然会出现这种情况)。
  • \(n[i]==0\ \&\&\ x[i]==0\),无要求。
  • \(n[i]==1\ \&\&\ x[i]==0\),此时至少有一个参与运算的数字第\(i\)位为0,那么\(l = max(l, ((n >> i) + 1 << i))\),此时在\(l\)的左边必定无解。
  • \(n[i]==0\ \&\&\ x[i]==1\),此时所有参与运算的数字第\(i\)位为1,那么\(r = min(r, ((n >> i) + 1 << i) - 1)\),在\(r\)右边无解。

最后若\(l <= r\)则输出\(l\)(\(l\)记录的是检验每个二进制位后的可行解),否则无解。

参考代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
void work()
{
    ll n, x;
    cin >> n >> x;
    ll l = n, r = 5e18;
    if (n < x)
    {
        cout << "-1" << endl;
        return ;
    }
    if (n == x)
    {
        cout << n << endl;
        return ;
    }
    bitset<64> a(n), b(x);
    for (int i = 63; i >= 0; --i)
    {
        if (a[i] == 0 && b[i] == 0)
            continue;
        else if (a[i] == 0 && b[i] == 1)
        {
            cout << "-1" << endl;
            return ;
        }
        else if (a[i] == 1 && b[i] == 0)
        {
            l = max(l, ((n >> i) + 1 << i));
        }
        else
        {
            r = min(r, ((n >> i) + 1 << i) - 1);
        }
    }
    if (l <= r)
    {
        cout << l << endl;
    }
    else
    {
        cout << "-1" << endl;
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;
    cin >> T;
    while (T--)
    {
        work();
    }
    return 0;
}

D. Friendly Spiders(数论,图论)

题目大意
给定一张有向图,边的权值为1,每个节点\(i\)的权值为\(a_i\),只有\(gcd(a_i,a_j)\neq 1\)的两点之间有边。
求给定两点之间的最短路径。

解题思路
求最短路径很简单,用\(bfs\)即可,关键是如何快速建边。
将原来的图改造成二分图,左边是原来的点,右边是质因子构成的集合,将左边的点与其所有的质因子连线。
原来的图结构事实上并没有被破坏,只是两点之间的距离变为了原来的两倍。但这大大减少了边的数量。

参考代码

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int pri[N], cnt_p;
int book[N];
int n, s, t;
struct 
{
    int v, next;
}e[N * 20];
int cnt, head[N * 2];//质数偏移量加N
void get_p()
{
    for (int i = 2; i < N; ++i)
    {
        if (!book[i])
        {
            pri[cnt_p++] = i;
        }
        for (int j = 0; pri[j] * i < N; ++j)
        {
            book[pri[j] * i] = 1;
            if (i % pri[j] == 0)
                break;
        }
    }
}
void add(int u, int v)
{
    e[++cnt].next = head[u];
    e[cnt].v = v;
    head[u] = cnt;
}
int trace[N * 2], dis[N * 2];
bool vis[N * 2];
queue<int> q;
bool bfs()
{
    q.push(s);
    vis[s] = 1;
    int v;
    while (q.size())
    {
        int u = q.front();
        if (u == t)
            return true;
        q.pop();
        for (int i = head[u]; i; i = e[i].next)
        {
            v = e[i].v;
            if (vis[v])
                continue;
            dis[v] = dis[u] + 1;
            trace[v] = u;
            vis[v] = 1;
            q.push(v);
        }
    }
    return false;
}
void out(int u)
{
    if (u == s)
    {
        cout << s;
        return ;
    }
    else
    {
        out(trace[u]);
        if (u < N)
            cout << " " << u;
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    get_p();
    cin >> n;
    int tt;
    for (int i = 1; i <= n; ++i)
    {
        cin >> tt;
        for (int j = 0; j < cnt_p && pri[j] * pri[j] <= tt; ++j)
        {
            if (tt % pri[j] == 0)
            {
                add(i, pri[j] + N);
                add(pri[j] + N, i);
                while (tt % pri[j] == 0)
                {
                    tt /= pri[j];
                }
            }
        }
        if (tt > 1)
        {
            add(i, tt + N);
            add(tt + N, i);
        }
    }
    cin >> s >> t;
    if (bfs())
    {
        cout << dis[t] / 2 + 1 << endl;
        out(t);
    }
    else
    {
        cout << "-1" << endl;
    }
    return 0;
}

E. The Human Equation(贪心)

题目大意
给定一个数组\(a\),求将其全部变为0的最少操作数。
可以进行的操作如下:
选择一个子序列

  • 偶数位置元素加1,奇数位置元素减1
  • 偶数位置元素减1,奇数位置元素加1

解题思路
求得其前缀和数组\(p\),假设选择两个位置\(i,j(i<j)\),令\(a[i]+=1,\ a[j]-=1\),在前缀和中体现为\(p[i... j]+=1\),上述操作等价为在前缀和数组中选择任意个元素加1或减1,而将原数组变为0等价于将前缀和数组变为0。
最终的答案为前缀和数组中大于0的最大元素-小于0的最小元素。

参考代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
void work()
{
    ll t, p = 0;
    ll ma = 0;
    ll mi = 0;
    int n;
    cin >> n;
    for (int i = 0; i < n; ++i)
    {
        cin >> t;
        p += t;
        mi = min(mi, p);
        ma = max(ma, p);
    }
    cout << ma - mi << endl;
    return ;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;
    cin >> T;
    while (T--)
    {
        work();
    }
    return 0;
}
posted @ 2023-01-14 21:53  何太狼  阅读(15)  评论(0编辑  收藏  举报