Codeforces Round 609 Div2

A

题意

找出两个合数使得他们差为某给出的数n

思路

先指定一个小的合数a,则b=a+n。若b是合数,则直接输出。若b是素数,则b+1一定不是素数。那么选一个a使得a,a+1均为合数即可。

代码

#include<bits/stdc++.h>
using namespace std;

bool check(int x)
{
    for(int i=2;i*i<=x;i++)
        if(x%i==0)return 0;
    return 1;
}
int main()
{
    int n,a=8,b;
    scanf("%d",&n);
    b=a+n;
    while(check(a)||check(b))
        a+=1,b+=1;
    printf("%d %d\n",b,a);
}

B

题意

给出两个数列a,b,一个整数m,求一个最小的x,使得a中所有元素加x再模m之后与b相等。

思路

要使得a最后与b相等,则a[0]必然会变成b中的一个元素,所以x的所有可能性一共2000种。所以O(n^2)的暴力测试所有可能性就好了。

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX=2005;

vector<int>p;
int a[MAX],b[MAX],c[MAX],n,m;

bool check(int x)
{
    for(int i=0; i<n; i++)
        c[i]=(a[i]+x)%m;
    sort(c,c+n);
    for(int i=0; i<n; i++)
        if(c[i]!=b[i])return 0;
    return 1;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0; i<n; i++)
        scanf("%d",&a[i]);
    for(int i=0; i<n; i++)
        scanf("%d",&b[i]);
    sort(a,a+n);
    sort(b,b+n);
    for(int i=0; i<n; i++)
    {
        int aa=a[i],bb=b[0];
        if(aa<bb)p.push_back(bb-aa);
        if(aa>bb)p.push_back(bb+m-aa);
    }
    p.push_back(0);
    sort(p.begin(),p.end());
    unique(p.begin(),p.end());
    for(int i=0; i<p.size(); i++)
        if(check(p[i]))
        {
            printf("%d\n",p[i]);
            break;
        }
}

C

题意

以数位的形式给出一个n位数a,再给出一个整数k,求一个最小的m位数b,使得b>a,并且对于任意i | 1<=i<=m-k,有b[i]==b[i+k]

思路

题意翻译一下就是b是由一个k位的数循环构成,尾部可以不足k位,所以只需要b的前k位大于等于a的前k位。要求b尽可能小,所以先用等于a的前k位构造b,再暴力比较一下,如果合法就输出,否则用a的前k位再加1构造,加一后可能存在进位,需要处理一下,但是不可能超过n位,因为999...这种情况一定能保证大于等于a

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+5;

char a[MAX],b[MAX];

int main()
{
    int n,k;
    scanf("%d%d%s",&n,&k,a+1);
    printf("%d\n",n);
    for(int i=1; i<=k; i++)
        b[i]=a[i];
    for(int i=k+1; i<=n; i++)
        b[i]=b[i-k];
    bool flag=1;
    for(int i=1; i<=n; i++)
    {
        if(b[i]>a[i])
            break;
        if(b[i]<a[i])
        {
            flag=0;
            break;
        }
    }
    if(flag)
    {
        printf("%s\n",b+1);
        return 0;
    }
    for(int i=k;i>0;i--)
    {
        if(b[i]=='9')
            b[i]='0';
        else
        {
            b[i]++;
            break;
        }
    }
    for(int i=k+1; i<=n; i++)
        b[i]=b[i-k];
    printf("%s\n",b+1);
}

D

题意

给出一个由1x1的正方形组成的图形,左右下平整。求这个图形中最多能填充多少个1x2的骨牌。

思路

思维神题。貌似很dp,实际上只需要把这个图形涂成象棋棋盘一样的黑白格。设白色少于黑色(多于黑色交换一下就行)。则有:

  1. 任意黑色只与白色相邻,反之亦然。
  2. 黑色多于白色
  3. 一个骨牌需要一黑一白

则由3可知,res=min(b,w),再由1,2可知所有白色格子一定可以匹配到一个黑色格子。所以可以推出答案就是白色的格子个数。

代码

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n,x;
    long long b=0,w=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&x);
        b+=(x/2);
        w+=(x/2);
        if(i&1)b+=(x&1);
        else w+=(x&1);
    }
    printf("%I64d\n",min(b,w));
}

E

题意

给出一个长度为n的排列p,每次操作可以交换相邻两数的位置。求对于k属于1-n的所有k,使得p中出现1-k的排列的最小操作次数(每个k独立)。

思路

对于连续的排列,则很明显就是求逆序数,但是本题难点在于k<n时,排列可能是被隔开的。所以需要先凑到一起。凑的话一定是两边向中间凑操作数最小。假设现在有k个数需要凑到一起。设数i当前的位置为s[i],凑到一起后的位置为t[i],凑到一起需要的操作次数为sum。则有:
\[ \begin{equation} \begin{aligned} sum&=\sum_{1}^k|s{[i]-t[i]|}\\ \\ sum&=\sum_{s[i]>t[i]} s[i]-t[i]+\sum_{t[i]>s[i]}t[i]-s[i]\\\\ &=\sum_{right}s[i]-\sum_{right}t[i]+\sum_{left}t[i]-\sum_{left}s[i]\quad(1) \end{aligned} \end{equation} \]
对于1式中中间两项,可以通过求出最中间的那个数的位置midpos来求出,左侧的t[i]一定是midpos-1,midpos-2......,右侧则为midpos+1,midpos+2......,则可以通过等差数列求和公式快速求出。而对于s[i],则可以通过树状数组维护,更新就在s[i]位置加s[i],这样求区间和就可以O(logn)的得到1式中的左右两项。

而对于midpos,可以用set来维护,根据新插入数据在之前midpos的左右来更新midpos,保证midpos始终合法。

所以总共用两个树状数组,一个维护逆序数,一个维护区间和,再用一个set维护midpos,总时间复杂度O(nlogn)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX=2e5+5;

int a[MAX],p[MAX],n;
ll c1[MAX],c2[MAX];
set<int>st;

void add(ll *c,int x,int k)
{
    for(;x<=n;x+=x&-x)
        c[x]+=k;
}
ll query(ll *c,int x)
{
    ll sum=0;
    for(;x;x-=x&-x)
        sum+=c[x];
    return sum;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),p[a[i]]=i;
    st.insert(p[1]);
    auto it=st.begin();
    printf("0 ");
    add(c1,p[1],1);
    add(c2,p[1],p[1]);
    ll res=0;
    for(int i=2;i<=n;i++)
    {
        st.insert(p[i]);
        if(p[i]<(*it)&&i%2==0)it--;
        if(p[i]>(*it)&&i%2==1)it++;//保证偶数时右侧始终多于左侧1个,以保证后面的正确性。
        add(c1,p[i],1);
        res+=(i*1ll-query(c1,p[i]));
        add(c2,p[i],p[i]);
        int midpos=*it;
        ll sum=0,k=i/2;
        sum+=i&1?k*(midpos-1+midpos-k)/2:(k-1)*(midpos+midpos-k)/2;
        sum-=k*(midpos+1+midpos+k)/2;
        sum-=query(c2,midpos-1);
        sum+=query(c2,n)-query(c2,midpos);
        printf("%I64d ",res+sum);
    }
}

猜你喜欢

转载自www.cnblogs.com/cryingrain/p/12431556.html