ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Codeforces Round #613(div2)解题报告

2020-01-18 21:01:22  阅读:433  来源: 互联网

标签:gcd int 613 ll Codeforces long maxn 互质 Round


Codeforces Round #613(div2)解题报告

A. Mezo Playing Zoma

  • 好像直接输出\(n+1\)就行了?那我应该写复杂了。
  • 懒得改了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T;

int main()
{
    int n;
    cin >> n;
    string str;
    cin >> str;
    int n1 = 0, n2 = 0;
    for(int i = 0; i < str.size(); i++)
    {
        if(str[i] == 'L') n1++;
        else n2++;
    }
    int ans = n1 + n2 + 1;
    cout << ans << endl;
    return 0;
}

B. Just Eat It!

  • 首先求出所有数字的和。
  • 对于另一个人选择子段,那么其实就是求最大子段和,属于入门dp问题。
  • \(f(i)=max(a(i),f(i-1)+a(i))\)。
  • 那么求出最大子段和,同时遍历出这个子段和最短的可能的区间,然后判定。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int T, n;
ll a[maxn], sum, f[maxn], s[maxn];

void solve()
{
    sum = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        f[i] = s[i] = 0;
    for(int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        sum += a[i];
        s[i] = s[i-1] + a[i];
    }

    ll maxx = -0x3f3f3f3f;
    for(int i = 1; i <= n; i++)
    {
        f[i] = max(a[i], f[i-1] + a[i]);
        maxx = max(maxx, f[i]);
    }
    int r = 0, l = 0;
    //正序遍历保证区间最短
    for(int i = 1; i <= n; i++)
        if(f[i] == maxx)
        {
            r = i;
            break;
        }

    for(int i = r; i >= 1; i--)
        if(s[r] - s[i-1] == maxx)
        {
            l = i;
            break;
        }

    if(sum > maxx)  puts("YES");
    else {
        if(sum == maxx)
        {
            if(l == 1 && r == n) puts("YES");
            else puts("NO");
        }
        else puts("NO");
    }
}

int main()
{
    scanf("%d", &T);
    while(T--) solve();
    return 0;
}

C. Fadi and LCM

  • 给定一个数字\(x\leq10^{12}\),求两个数字\(a,b\)使得\(max(a,b)\)尽可能的小且\(lcm(a,b)=x\)。
  • 好像正解是分解质因数后枚举方案?
  • 仔细想了一下的话就会发现:
    • 首先,两个数字要互质。
    • 第二:两个数字尽可能的接近。
  • 为了让两个数字尽量接近,那么就可以直接开个根号,然后往前枚举,开根号后数据是\(10^6\),\(gcd\)的复杂度在\(log\),可以通过。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
ll gcd(ll a, ll b)
{
    if(b == 0) return a;
    return gcd(b, a%b);
}
int main()
{
    ll x; cin >> x;
    if(x == 1) {
        cout << "1 1" << endl;
        return 0;
    }
    ll a = sqrt(x);
    while(1)
    {
        ll b = x / a;
        if(a * b == x && gcd(a, b) == 1){
            cout << a << " " << b << endl;
            return 0;
        }
        a--;
    }
    return 0;
}

D. Dr. Evil Underscores

  • 给定一个序列\(a_i\),找一个数字使得\(max_{1\leq i\leq n}a_i\land X\)最小,求出这个最小值。
  • 贪心+分治。
  • 从最高位开始贪心,如果此时数组元素的这一位全为0或者全为1,那么可以把它们全部贪掉。
  • 如果有的是1,有的是0,那说明这一位是躲不掉的,这一位肯定为1,所以在返回的时候加上一个\(1<<i\)。
  • 而对于后面的数字分治取最小值即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int> a;
int n;

int solve(vector<int> v, int bit)
{
    if(bit < 0 || v.size() == 0) return 0;
    vector<int> z, o;
    for(int i = 0; i < v.size(); i++)
    {
        if((v[i]>>bit) & 1) z.push_back(v[i]);
        else o.push_back(v[i]);
    }
    if(o.size() == 0) return solve(z, bit-1);
    if(z.size() == 0) return solve(o, bit-1);
    return (min(solve(o, bit-1), solve(z, bit-1))|1<<bit);
}

int main()
{
    scanf("%d", &n);
    for(int i = 1, x; i++ <= n;)
        scanf("%d", &x), a.push_back(x);
    cout << solve(a, 30) << endl;
}

E. Delete a Segment

  • 给你n个线段,union of the set of segments指的是几个线段两两之间互不相交的线段个数。
  • 问准确的拿走一个线段,问拿走哪个线段,剩余的线段组成的union of the set of segments数量最大。
  • 洛谷题解
  • 暴力做法是枚举删除的线段后\(O(n)\)扫一遍,利用并查集判断最后有几个线段。
  • 但其实可以先求出所有的线段答案,之后枚举删除的线段。
  • 对于一个线段,删除后有可能造成产生更多的结果。
  • 所以枚举之后记录一下最大的增长量,加上最初的结果,就是最终的结果。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;

int n;
struct segment{
    ll l, r;
}s[maxn];
ll ans0, ans1;
ll tmp[maxn<<1], d_[maxn<<2], d[maxn<<2];

void solve()
{
    ans0 = 0;
    ans1 = -0x3f3f3f3f;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d%d", &s[i].l, &s[i].r);
        tmp[(i<<1)-1] = s[i].l;
        tmp[i<<1] = s[i].r;
    }
    sort(tmp+1, tmp+(n<<1)+1);
    int tot = unique(tmp+1, tmp+(n<<1)+1) - tmp - 1;
    for(int i = 1; i <= n; i++)
    {
        s[i].l = lower_bound(tmp+1, tmp+1+tot, s[i].l) - tmp;
        s[i].r = lower_bound(tmp+1, tmp+1+tot, s[i].r) - tmp;
        s[i].l <<= 1ll, s[i].r <<= 1ll;
        d[s[i].l]++, d[s[i].r+1]--; //差分序列
    }
    tot <<= 1;
    for(int i = 1; i <= tot+5; i++) 
        d[i] += d[i-1];
    //初始答案
    for(int i = 0; i <= tot+5; i++) 
        ans0 += d[i] && !d[i+1];
    for(int i = 0; i <= tot+4; i++)
        if(d[i] == 1 && d[i+1] != 1)
            d_[i]++;
    for(int i = 1; i <= tot+5; i++)
        if(d[i] == 1 && d[i-1] != 1)
            d_[i]++;
    for(int i = 1; i <= tot+5; i++)
        d_[i] += d_[i-1];
    for(int i = 1; i <= n; i++)
    {
        ll t = (d_[s[i].r] - d_[s[i].l-1]) / 2;
        t -= (d[s[i].r] == 1) + (d[s[i].l] == 1);
        ans1 = max(ans1, t);
    }
    cout << ans0 + ans1 << endl;
    for(int i = 0; i <= tot+10; i++)
        d[i] = d_[i] = 0;
}

int main()
{
    int T; scanf("%d", &T);
    while(T--) solve();
    return 0;
}

F. Classical?

题意:

  • 给定\(n\)个数,求\(max_{1\leq i\leq j\leq n}lcm(a_i,a_j)\)。
  • 数据范围:\(n,a_i\leq 1e5\)。

思路:

  • 我们知道\(lcm(a,b)=\frac{ab}{gcd(a,b)}\)。

  • 考虑枚举\(gcd(a,b)=1,2,3,...=g\)。

  • 那么我们现在考虑考虑\(g\)的倍数,将这些倍数全部除以\(g\),那么有\(gcd(x,y)=g=>gcd(x/g,y/g)=1\)。

  • 所以其实我们只用考虑互质的那种情况,不互质可以转换成互质。

  • 从大到小扫描集合里的数。

  • 假如说扫描到有个数字\(x\),存在\(y\)有\(y>x\)且\(gcd(x,y)=1\),那么所有满足\(x<z<y\)的数\(z\)不会再对答案有贡献了。因为\(gcd(z,x)\)可能不等于\(1\),而且\(z*x\)一定小于\(x*y\)。

  • 因此用栈存储扫过的元素,扫到一个数\(x\)时,只要栈中有与\(x\)互质的数就pop,并与\(x\)更新答案。

  • 如果判断栈中是否有与x互质的元素?

  • 与\(x\)互质的数的个数为:

    • \(\phi(x)\)。
    • 有\(\sum_{d|n}\frac{\mu(d)}{d}=\frac{\phi(n)}{n}\).
    • 有\(\phi(n)=\sum_{d|n}\mu(d)*\frac{n}{d}\)。
    • 假设\(cnt_d\)表示栈中\(d\)的倍数数量。
    • 那么栈中与\(x\)互质的数的数量有:

    • \(\sum_{d|x}\mu(d)cnt_d\)。
    • 其中\(\mu(d)\)是莫比乌斯函数。

  • 所以只需要遍历完这些与\(x\)互质的数,然后取最大的就行了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int n, cnt[maxn];
bool b[maxn], vis[maxn];
vector<int> d[maxn];
ll ans;

ll gcd(ll a, ll b)
{
    if(b == 0) return a;
    return gcd(b, a%b);
}

int mu[maxn], primes[maxn], tot;
void get_mu(int n)
{
    mu[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        if(!vis[i])
        {
            primes[++tot] = i;
            mu[i] = -1;
        }
        for(int j = 1; primes[j] <= n/i; j++)
        {
            vis[primes[j]*i] = 1;
            if(i % primes[j] == 0) break;
            else mu[i*primes[j]] = -mu[i];
        }
    }
    //预处理出所有倍数
    for(int i = 1; i <= n; i++)
        for(int j = i; j <= n; j += i)
            d[j].push_back(i);
}

ll coprime(ll x)
{
    ll res = 0;
    for(int i = 0; i < d[x].size(); i++)
        res += cnt[d[x][i]] * mu[d[x][i]];
    return res;
}

void update(int x, int a)
{
    for(int i = 0; i < d[x].size(); i++)
        cnt[d[x][i]] += a;
}

int main()
{
    get_mu(maxn-10);

    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
    {
        ll x; scanf("%lld", &x);
        ans = max(ans, x);
        b[x] = 1;
    }

    stack<int> s;
    //枚举gcd=g
    for(int g = 1; g <= maxn-10; g++)
    {
        for(int i = (maxn-10)/g; i > 0; i--)
        {
            //没有这个数字 跳过
            if(!b[i*g]) continue;
            //c表示栈中与i互质的数的数量
            int c = coprime(i);
            while(c)
            {
                if(gcd(s.top(), i) == 1)
                {
                    ans = max(ans, 1ll*i*s.top()*g);
                    c--;
                }
                //出栈
                update(s.top(), -1);
                s.pop();
            }
            update(i, 1);
            s.push(i);
        }
        //空栈
        while(s.size())
        {
            update(s.top(), -1);
            s.pop();
        }
    }
    cout << ans << endl;
    return 0;
}

标签:gcd,int,613,ll,Codeforces,long,maxn,互质,Round
来源: https://www.cnblogs.com/zxytxdy/p/12210186.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有