每一年都奔走在自己热爱里

没有人是一座孤岛,总有谁爱着你

Codeforces Round 752 (Div. 2)(C,D,E)

Codeforces Round 752 (Div. 2)(C,D,E)

C

C

这道题的大意就是给你n个数,然后我们可以选择第i个数,如果ai,我们就可以删除这一个数,每次删除之后每一个数的下标都会更新,然后问我们是否可以把这n个数都删除

对于每一个数,我们可能有这些数成为他们的下标,对于i,从2i+1都可能成为这个数的下标,然后我一开始觉得直接模拟就会爆掉,其实不然,因为lcm(2,3,4,5...32)>1e9,所以我们最多枚举到30多就可以了

然后我们就直接判断从2i+1是否存在不可整除的数,如不存在就一定删除不了

#include <iostream>
#include <algorithm>
using namespace std;
#define int long long 
const int maxn=1e5+10;
int t,n;
int a[maxn];
void solve()
{
    cin>>n;
    bool can=true;
    for (int i=1;i<=n;i++)
    {
        cin>>a[i];
        bool yes=false;
        for (int j=2;j<=i+1;j++)
        {
            if (a[i]%j)
            {
                yes=true;
                break;
            }
        }
        if (!yes) can=false;
    }
    if (can)
    {
        cout<<"YES\n";
    }
    else cout<<"NO\n";
    return ;
}
signed main ()
{
    cin>>t;
    while (t--)
    {
        solve();
    }
    system ("pause");
    return 0;
}

D

D

这个虽然做出来了,但是还想记录一下

题目给我们两个数,xy,求找到一个n,满足n

这个可以分类讨论

如果x==y,n=x,那么n

如果x>y,那么n=x+y,那么n,y

如果x<y

然后我们就可以写代码了

#include <iostream>
#include <algorithm>
using namespace std;
#define int long long 
int t,n;
void solve()
{
    int x,y;
    cin>>x>>y;
    int ans;
    if (x==y)
    {
        ans=x;
    }
    else if (x>y)
    {
        ans=x+y;
    }
    else 
    {
        int p=x;
        x=y/x;
        x=x*p;
        ans=(x+y)/2;
    }
    cout<<ans<<'\n';
    return ;
}
signed main ()
{
    cin>>t;
    while (t--)
    {
        solve();
    }
    system ("pause");
    return 0;
}

E

E

这个题大意是给你n个数,然后我们我们选择这n个数里面的子数组,可以选择一个数k,把ak变成xy,满足x+y=ak,使得这个子数组变成非递减的数组,每一个最后得到的非递减数组经历了x次操作,然后就得到一个价值x,问这n个数可以得到多少价值

因为我们要满足是非递减数组,所以我们可以根据后面的,得到前面的

对于此时的ai,然后我们可以根据以ai这个位置可以得到前面一个位置的数可以是什么

比如此时以ai+1为结尾的,那么我们选择在这个ai+1的前面的那一项的大小可以是多少

我们要满足这个数是满足小于等于ai+1,那么它前面的那些数比ai+1小的

我们可以得到此时的cur,我们还可以得到上一轮的得到最前面的值为cur的分配方法dp[i1][cur],然后i的使用程度是i次,(如对于(1,i+1),(2,i+1)...(i,i+1)),然后我们还需要把ai分成v堆的操作数(v1),

我们要求的是满足要求的子数组的价值和

所以我们对于分开ai的贡献为到达这一种子数组的子数组数量乘以这一实现这一个子数组的操作数然后乘以他们每一个位置的使用程度

那么

我们可以得到每一种分开方式的贡献

ans=ans+i×dp[i1][cur]×(v1)

然后这些数组的转移还需要考虑

对于此时的ai变成若干个cur,但是ai不一定能整除

所以对于在ai+1的前面的那些数的排列可能是cur1,cur1,cur,cur这种的

所以我们还需要判断在这种分配方式的最前面的那一个数是a[i]/v

可以得到状态转移方程为

dp[i][next]+=dp[i1][cur]

但是由于这个题的n有点大,开二维空间不够

所以我们这里使用了二进制的变化来代表此时的dp和前一轮的dp值,实际上我们也只需要这两个dp

然后具体看代码

#include <iostream>
#include <algorithm>
using namespace std;
#define int long long 
const int maxn=1e5+10;
const int mod=998244353;
int t,n;
int a[maxn];
int dp[2][maxn];
void solve()
{
    cin>>n;
    for (int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    int ans=0;
    dp[n&1][a[n]]=1;//初始条件a[n]
    int now=n&1;
    for (int i=n-1;i>=1;i--)//a[n]是不需要变化的,(变化就变小了),往前遍历
    {
        for (int j=1;j<=a[i+1];)//cur a[i+1],cur有多少种,这个是从最大的往小的来
        {
            int cur=a[i+1]/j;//cur a[i+1]这样的排列
            int u=dp[now][cur];//到达cur有多少种办法
            int v=(a[i]+cur-1)/cur;//v是最后分成了多少堆,那么我们需要v-1次操作
            int next=a[i]/v;// next next ,cur cur,next是最左边的状态
            ans=(ans+i*u%mod*(v-1)%mod)%mod;//对于cur a[i+1],有i种组合,(1,i+1),(2,i+1)....(i,i+1)
            dp[now^1][next]+=u;//cur到next的方式又加多了u种
            dp[now][cur]=0;
            j=a[i+1]/cur+1;//下一种cur,cur递降
        }
        dp[now^1][a[i]]++;//以a[i]直接作为结尾
        now^=1;
    }
    cout<<ans<<'\n';
    for (int i=1;i<maxn;i++)//记得重新变成0,下一轮才不会出错
    {
        dp[0][i]=dp[1][i]=0;
    }
    return ;
}
signed main ()
{
    cin>>t;
    while (t--)
    {
        solve();
    }
    system ("pause");
    return 0;
}
posted @   righting  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示