CF 做题记录

前言

Codeforces Round 836 (Div. 2)

C. Almost All Multiples

这题挺妙的。

很容易发现 n 时无解。

p[x]=n,p[1]=x,p[n]=1 就是一个可行解。

但此题要求字典序最小,我们以 8,2 为例。

现在的序列:

2 8 3 4 5 6 7 1

字典序最小的序列:

2 4 3 8 5 6 7 1 

可以发现每次都把 n 向后移,而且这个可以交换的位置构成了一个链:x,x1,x2n,这个链的所有值满足 p[xi]modxi1=0,p[xi1]modxi=0

按照上面的判定条件跑一遍即可。

code

D. Range = √Sum

我们先看当 n 为偶数的情况。

mid=n/2,可以构造出 mid,mid+1,n1,n+1,n+1,n+mid

这里很好证明。

maxmin=n

sum=(mid+mid+n)×(n+1)2n=n2

sqrtsum=maxmin=n

那把它推展到奇数。

观察一下 4 构成的序列:

2 3 5 6 

想办法变成 3 个数,最好操作的就是把 3 去掉,其余的加一。变成:

3 6 7

满足条件。

推广到整个正奇数集,先构造出 n+1 的序列,然后把 n 去掉,其余的加一。

证明:

因为 n 一定被包含于 n+1 的序列中,可以去掉 n

为了保持总和不变,剩下的 n 个数都加一。

由于 n+1 的序列中的数各不相同,都加一后也一定不同,保证了 n 的序列的不重性。

code

E. Tick, Tock

待补。

F. Decent Division

待补。

Codeforces Round 840 (Div. 2)

C.Another Array Problem

可以发现:

  • n=2ans=max(a[1]+a[2],2abs(a[1]a[2]))

  • n=3ans=max(1lla[1]+1lla[2]+1lla[3],max(1ll3a[1],max(1ll3abs(a[2]a[3]),max(1ll3abs(a[1]a[2]),1lla[3]n))))

  • n>3ans=maxxn

因为只要对一个区间连续两次操作就可以变成 0n>3 时无论怎么变都不如把所有数变成最大值(有点不好发现)。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, maxx;
int a[N];
void solve()
{
    scanf("%d", &n);
    maxx = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        maxx = max(maxx, a[i]);
    }
    if (n >= 4)
        return printf("%lld\n", 1ll * n * maxx), void();
    if (n == 2)
        return printf("%d\n", max(a[1] + a[2], 2 * abs(a[1] - a[2]))), void();
    if (n == 3)
        return printf("%lld\n", max(1ll * a[1] + 1ll * a[2] + 1ll * a[3], max(1ll * 3 * a[1], max(1ll * 3 * abs(a[2] - a[3]), max(1ll * 3 * abs(a[1] - a[2]), 1ll * a[3] * n))))), void();
    printf("%d\n", a[1]);
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
        solve();
}

Codeforces Round 948 (Div. 2)

B. Binary Colouring

卡我一个小时。

先二进制拆分,求出 01 序列。

考虑有相邻的如 14

0,1,1,1

可以知道:2x+2x+1=2x+22x

那上序列可改为:0,1,0,2

有知道 2×2x=2x+1

序列又可改为 0,1,0,0,1

此题解决。

#include<bits/stdc++.h>
using namespace std;
int n;
int a[40];
void solve()
{
    scanf("%d",&n);
    memset(a,0,sizeof a);
    int now=n,m=0;
    for(int i=31;i>=0;i--)
    {
        if(now-(1<<i)>=0)
        {
            now-=(1<<i);
            a[i]=1;
            m=max(m,i);
        }
    }
    for(int i=0;i<=m;i++)
    {
        if(a[i]==2) a[i]=0,a[i+1]++;
        if(a[i]==1&&a[i+1]==1) 
        {
            a[i]=-1,a[i+1]=0,a[i+2]++;
        }
    }
    if(a[m+1])    m++;
    printf("%d\n",m+1);
    for(int i=0;i<=m;i++)
        printf("%d ",a[i]);
    puts("");
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--) solve();
    return 0;
}     

C. Nikita and LCM

先排序,计算所有值的 lcm 记为 ss,当 ssa[n],或再计算时就大于 109,答案为 n

由于 ss 一定小于 109,可以枚举 ss 的因数作为序列的 lcm,所有它的因数加进来只会使最小公倍数大,且不会超过枚举的最小公倍数。

最后判断是否与枚举 lcm 的相同,记录答案即可。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2020;
int n, a[N], ans;
map<long long, int> vis;
int gcd(int x, int y)
{
    if (!y)
        return x;
    return gcd(y, x % y);
}
void check(int x)
{
    int res = 1, cnt = 0;
    for (int i = 1; i <= n; i++)
    {
        if (x % a[i] == 0)
        {
            int d = gcd(res, a[i]);
            res = res / d * a[i];
            cnt++;
        }
    }
    if (res == x)
        ans = max(cnt, ans);
}
void solve()
{
    scanf("%lld", &n);
    vis.clear(), ans = 0;
    int ss = 1;
    for (int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    sort(a + 1, a + 1 + n);
    for (int i = 1; i <= n; i++)
    {
        vis[a[i]] = 1;
        int d = gcd(ss, a[i]);
        ss = ss / d;
        ss *= a[i];
        if (ss > 1e9)
        {
            printf("%lld\n", n);
            return;
        }
    }

    if (ss != a[n])
    {
        printf("%lld\n", n);
        return;
    }
    for (int i = 1; i * i <= ss; i++)
    {
        if (ss % i == 0)
        {
            if (!vis[i])
                check(i);
            if (!vis[ss / i])
                check(ss / i);
        }
    }
    printf("%lld\n", ans);
}
signed main()
{
    int T;
    scanf("%lld", &T);
    while (T--)
        solve();
    return 0;
}

D. XORificator

很有意思的一道题。

Codeforces Round 939 (Div. 2)

D. Nene and the Mex Operator

有意思的题。

本题的 trick 是每一个 [l,r] 的区间都可以让这个区间的每一个数变为:rl+1

由于此题 n 很小,可以暴力枚举 2n 种情况,哪些要使用 mex 操作,求出最大值和要操作的区间。

对于每一个区间。试着手动操作,可以发现 [l,r1] 完成可以使 [l,r] 完成。

这里递归求解操作即可。

#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
int n;
int a[20],vis[20];
vector<pii>way;
void domex(int l,int r)
{
    memset(vis,0,sizeof vis);
    for(int i=l;i<=r;i++)   vis[a[i]]++;
    int mex=0;
    while(vis[mex]) mex++;
    for(int i=l;i<=r;i++)   a[i]=mex;
    way.push_back({l,r});
}
void solve(int l,int r)
{
    if(l==r)
    {
        if(a[l])    domex(l,r);
        return ;
    }
    solve(l,r-1);
    if(a[r]!=r-l) 
        domex(l,r),solve(l,r-1);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int fi=(1<<n)-1,ans=0,cur;
    for(int i=0;i<=fi;i++)
    {
        int l=1,res=0;
        while(l<=n)
        {
            int r=l;
            if(i&(1<<l-1))
            {
                while(r<=n&&i&(1<<r-1)) r++;
                res+=(r-l)*(r-l);
            }
            else res+=a[l],r=l+1;
            l=r;
        }
        if(res>ans) ans=res,cur=i;
    }
    //cout<<cur<<endl;
    int l=1;
    while(l<=n)
    {
        int r=l;
        if(cur&(1<<l-1))
        {
            while(r<=n&&cur&(1<<r-1)) r++;
            solve(l,r-1);
            domex(l,r-1);
        }
        else r=l+1;
        l=r;
    }
    
    printf("%d %d\n",ans,(int)way.size());
    for(auto t : way)   printf("%d %d\n",t.first,t.second);
    return 0;
}
posted @   houguo  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示