Codeforces Round #655 (Div. 2) (A-E)

题目:A. Omkar and Completion
直接构造相同的数。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 200005*4
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
const int mod=998244353;
const int MAXlen=1e5+10;
long double eps=1e-9;
int a[1010];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int i,n,j;
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            printf("1 ");
        }
        printf("\n");
    }
    return 0;
}

题目:B. Omkar and Last Class of Math

题意:a+b=n,找到min(LCM(a,b))。
直接去枚举gcd(a,b)的因子, t 1 ∗ x + t 2 ∗ x = n , l c m = t 1 ∗ t 2 ∗ x t1*x+t2*x=n,lcm=t1*t2*x t1x+t2x=n,lcm=t1t2x,而 t 2 ∗ t 1 t2*t1 t2t1最小的时候即有一位为1(即 ( y − t 1 ) ∗ t 1 (y-t1)*t1 (yt1)t1的最小值)。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 200005*4
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
const int mod=998244353;
const int MAXlen=1e5+10;
long double eps=1e-9;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ll i,n,j;
        ll a1,a2;
        scanf("%I64d",&n);
        a1=1,a2=n-1;
        ll ans=n-1;
        for(i=2;i*i<=n;i++)
        {
            if(n%i==0)
            {
                if(ans>(i-1)*(n/i))
                {
                    ans=(i-1)*(n/i);
                    a1=n/i*1,a2=n/i*(i-1);
                }
            }
        }
        printf("%I64d %I64d\n",a1,a2);
    }
    return 0;
}

题目:C. Omkar and Baseball

题意:求把给定数组变换为1-n的顺序排列,变换可选择一段区间,变换完不能在相同的位置有跟变换之前相同的数。

可以发现,如果变换完跟1-n这个顺序完全没有一个相同的,变换一次即可。
任何顺序的若初始有a[i]=i,那么多加一次变换,把a[i]=i的与任意a[i]!=i(或和a[i]=i)的交换,最多2次全换完。
特殊处理一下首尾的情况。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 200005*4
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
const int mod=998244353;
const int MAXlen=1e5+10;
long double eps=1e-9;
int a[200100];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,i,j;
        scanf("%d",&n);
        int sum=0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]==i)
                sum++;
        }
        if(!sum)
        {
            printf("1\n");
            continue;
        }
        else if(sum==n)
        {
            printf("0\n");
            continue;
        }
        for(i=1;i<=n;i++)
        {
            if(a[i]==i)
                sum--;
            else
                break;
        }
        for(i=n;i>=1;i--)
        {
            if(a[i]==i)
                sum--;
            else
                break;
        }
        if(!sum)
            printf("1\n");
        else
            printf("2\n");
    }
    return 0;
}

题目:D. Omkar and Circle

题意:给了一个长度为n的圆排列,拿走a[i],则a[i-1]和a[i+1]的和取代a[i]位置,直到还剩余一个数。求最大值。

首先肯定拿的次数为n/2,进而分析,拿的数字都不是相邻的,为什么?因为如果拿了相邻的原本是再整个排列的总和中减去一个数,拿相邻的就减去两个数了,不符合要最大的策略。式子表示就是sum-x1-x2-x3-x5<sum-x1-x5。
分成奇偶的情况进行枚举,中途可以有一次拿间隔是3的。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 200005*4
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
const int mod=998244353;
const int MAXlen=1e5+10;
long double eps=1e-9;
int a[400100];
ll sum1[200100];
ll sum2[200100];
int main()
{
    int n,i,j;
    ll sum=0;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sum+=a[i];
        a[n+i]=a[i];
    }
    sum1[0]=sum2[0]=0;
    for(i=1;i<=n;i++)
    {
        if(i&1)
        {
            sum1[i]=sum1[i-1]+a[i];
            sum2[i]=sum2[i-1];
        }
        else
        {
            sum1[i]=sum1[i-1];
            sum2[i]=sum2[i-1]+a[i];
        }
    }
    if(n==1)
    {
        printf("%d",a[1]);
        return 0;
    }
    int temp=n/2;
    ll ans=sum-sum2[n];
    for(i=1;i<n;i+=2)
    {
        ans=max(ans,sum-(sum1[i]+sum2[n]-sum2[i+1]));
    }
    for(i=n;i>1;i-=2)
    {
        ans=max(ans,sum-(sum1[n]-sum1[i-2]+sum2[i-2]));
    }
    for(j=2;j<=n;j+=2)
    {
        ans=max(ans,sum-(sum2[i]+sum1[n]-sum1[i+1]));
    }
    printf("%I64d\n",ans);
    return 0;
}

题目:E. Omkar and Last Floor
题意:给定了 n ∗ m n*m nm的矩阵,n为行数。给定每行的分割区间值,每个区间可以放一个1。求 ∑ i = 1 m q i 2 {\sum_{i=1}^mq_i}^2 i=1mqi2的最大值。

毫无疑问一列全满的时候贡献最大。区间dp,
设dp[i][j]:从i列到j列的最大值。这么直接做貌似写起来很麻烦。
每一段区间中的1都是可以转移的,所以当第k列的第某行的左右端点都在i-j中的时候直接找所有1集中的贡献。
dp[i][j]:表示i–j列区间所包含区间的最大贡献。
举个栗子:枚举第k列的时候,由于有3行该点的区间端点是i-j的子集,则我们可以将他们移动到一起,那么该区间dp[i][k-1]以及dp[k+1][j]就不能算他们的贡献了,所以变完有3行该点的区间端点就不是k+1–j和i–k+1的子集了就不能算贡献了。
女少口阿。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 200005*4
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
const int mod=998244353;
const int MAXlen=1e5+10;
long double eps=1e-9;
int a[110][110];
int dp[110][110];
int L[110][110],R[110][110];
int main()
{
    int n,m,i,j;
    scanf("%d %d",&n,&m);
    for(i=1;i<=n;i++)
    {
        int temp;
        scanf("%d",&temp);
        while(temp--)
        {
            int l,r;
            scanf("%d %d",&l,&r);
            for(j=l;j<=r;j++)
            {
                L[i][j]=l;
                R[i][j]=r;
            }
        }
    }
    for(int len=1;len<=m;len++)
    {
        for(i=1;i+len-1<=m;i++)
        {
            int RR=i+len-1;
            for(int k=i;k<=RR;k++)
            {
                int sum=0;
                for(j=1;j<=n;j++)
                {
                    if(L[j][k]>=i&&R[j][k]<=RR)
                    {
                        sum++;
                    }
                }
                dp[i][RR]=max(dp[i][RR],dp[i][k-1]+dp[k+1][RR]+sum*sum);
            }
        }
    }
    printf("%d",dp[1][m]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值