CF 做题记录

前言

Codeforces Round 836 (Div. 2)

C. Almost All Multiples

这题挺妙的。

很容易发现 $ n% x==0$ 时无解。

\(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,x_1,x_2 \cdots n\),这个链的所有值满足 \(p[x_i] \mod x_{i-1} = 0,p[x_{i-1}] \mod x_i=0\)

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

code

D. Range = √Sum

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

\(mid=n/2\),可以构造出 \(mid,mid+1,\cdots n-1,n+1,n+1,\cdots n+mid\)

这里很好证明。

\(max-min=n\)

\(sum=\dfrac{(mid+mid+n)\times (n+1)}{2}-n=n^2\)

\(sqrt{sum}=max-min=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=2\)\(ans=max(a[1] + a[2], 2 * abs(a[1] - a[2]))\)

  • \(n=3\)\(ans=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))))\)

  • \(n>3\)\(ans=maxx*n\)

因为只要对一个区间连续两次操作就可以变成 \(0\)\(n>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\)

可以知道:\(2^x+2^{x+1}=2^{x+2}-2^{x}\)

那上序列可改为:\(0,-1,0,2\)

有知道 \(2\times 2^x=2^{x+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\),当 \(ss \neq a[n]\),或再计算时就大于 \(10^9\),答案为 \(n\)

由于 \(ss\) 一定小于 \(10^9\),可以枚举 \(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]\) 的区间都可以让这个区间的每一个数变为:\(r-l+1\)

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

对于每一个区间。试着手动操作,可以发现 \([l,r-1]\) 完成可以使 \([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 @ 2024-06-06 16:23  hongou  阅读(9)  评论(0编辑  收藏  举报