2021牛客多校赛第一场

传送门

A博弈(队友过的)
B水题
C复杂的线段树(没看明白,待补)
D水题
E大模拟恶心的bfs
F水题
G思维题
H数论知识a=b+m如果m%x!=0那么a%x不等于b%x,另外需要用FFT
I求期望,概率DP(待补,概率DP专题)
J比C题简单的优美线段树(看明白了,题解和C一起写吧)
K出题人鼓励大家乱搞实际上也是乱搞


G
题意:给长度为N的A、B两组序列,要求交换A中的两个不同元素,恰好进行K次交换。使得A、B对应位置的数之差的绝对值加起来尽可能大,输出这个值

思考:
怎么交换才能让值变得更优呢?考虑有四个数,a>b>c>d ,它们组成数对。如果a对应的不是b,那么b就会对应c或者d。此时a、b都会因为在数对中较大而获得正号。那么此时四个数的符号分布就是+a,+b,-c,-d。此时已是最优解。所以只有当a对应b时,我们才有更改的必要,因为此时是+a,-b(c、d不确定也不用管)。也就是说,对于A与B形成的两个数对(a1,b1)和(a2,b2),只有当min(a1,b1)>max(a2,b2)时(或者min(a2,b2)>max(a1,b1),两者没有区别),我们才需要交换a1,a2来得到更优解。且此时值增加了2*(min(a1,b1)-max(a2,b2))
另外发现,当n>2时,最终解一定会存在两个数对中A元素符号都为正或者都为负,此时交换两个元素符号是没有任何影响的。所以是可以进行“无效交换”的,这点也就让题目的“恰好交换K次”变成了“最多交换K次”。

解析:因为交换满足min(a1,b1)>max(a2,b2)这一条件的数对时才有作用,且增加的值为2*(min(a1,b1)-max(a2,b2)),所以只需要取min(a,b)尽可能大的,以及max(a,b)尽可能小的,都取K个(如果满足条件的不足K个则能取多少是多少),再进行处理即可

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

ll a[500050],b[500050];
ll mi[500050],ma[500050];
ll ans=0;
int main()
{
    ll n,k;
    scanf("%lld%lld",&n,&k);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&b[i]);

    if(n==2)//考虑特殊情况
    {
        if(k%2==1)
            swap(a[1],a[2]);
        ans=abs(a[1]-b[1])+abs(a[2]-b[2]);
        printf("%lld\n",ans);
        return 0;
    }

    for(ll i=1;i<=n;++i)
        ans+=abs(a[i]-b[i]);
    for(ll i=1;i<=n;++i)
    {
        mi[i]=min(a[i],b[i]);
        ma[i]=max(a[i],b[i]);
    }
    sort(mi+1,mi+1+n);
    sort(ma+1,ma+1+n);

    for(ll i=1;i<=k&&i<=n;++i)
    {
        if(mi[n-i+1]>ma[i])   //min(a,b)取大的,max(a,b)取小的
            ans+=2*(mi[n-i+1]-ma[i]);
        else
            break;
    }

    printf("%lld\n",ans);
    return 0;
}



H
题意:给一个长度为N的非负整数序列,序列中数两两不相同,找一个最小数x,使得序列中所有数对x取余后都不同,N<=500000,ai<=500000

解析:取余不同则想到a=b+m,若m%x!=0,则a%x不同于b%x。那么要使得所有的数对x取余后两两互不相同,则需要所有的数两两之差都不能整除于x,且x最小。后面求x比较简单,算N个数两两之差当然就是用FFT了,利用多项式相乘即指数相加来做。(加减用指数,相乘用系数)因为是之差所以需要设一个偏移量。两个多项式一个指数直接是ai,一个指数是500050-ai。最后结果再-500050即可。(比赛的时候把前一个ai+500050结果T了半天最后发现根本没必要加这个500050…)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2e6+50;
const double pi=acos(-1);
ll r[N],bits;
complex<double>A[N],B[N],w[N];
int dp[500005], vis[500005];
void FFT(complex<double> *P,ll op,ll N)
{
    for(ll i=1;i<N;i++)
    {
        if(i>r[i])
            swap(P[i],P[r[i]]);
    }
    for(ll i=1;i<N;i<<=1)
    {
        for(ll p=i<<1,j=0;j<N;j+=p)
        {
            for(ll k=0;k<i;k++)
            {
                complex<double>W=w[N/i*k];
                W.imag(W.imag()*op);
                complex<double> X=P[j+k],Y=W*P[j+k+i];
                P[j+k]=X+Y;
                P[j+k+i]=X-Y;
            }
        }
    }
}
 
ll a[N],b[N],cnt[N];
 
void solve(ll *a,ll *b,ll n,ll m)
{
    ll len;
    for(len=1,bits=0;len<=n+m;len<<=1)
        bits++;
    bits--;
    for(ll i=0;i<len;i++)
        A[i].real(0),A[i].imag(0),B[i].real(0),B[i].imag(0);
    for(ll i=0;i<=n;i++)
        A[i].real(a[i]);
    for(ll i=0;i<=m;i++)
        B[i].real(b[i]);
    for(ll i=0;i<len;i++)
        r[i]=(r[i>>1]>>1)|((i&1)<<bits);
    for(ll i=0;i<len;i++)
        w[i].real(cos(pi/len*i)),w[i].imag(sin(pi/len*i));
    FFT(A,1,len);
    FFT(B,1,len);
    for(ll i=0;i<len;i++)
        A[i]=A[i]*B[i];
    FFT(A,-1,len);
    for(ll i=0;i<=n+m;i++)
        a[i]=(ll)(A[i].real()/len+0.5);
}
 
ll  hh[1500150];
ll cii=0;
 
int main()
{
    ll n,m,k,x;
    scanf("%lld",&n);
    for(ll i=0;i<n;i++)
    {
        scanf("%lld",&x);
        a[x]=1;
        b[500050-x]=1;
    }
    solve(a,b,500050,500050);
 
    //printf("a=%lld \n",a[1000]);
    for(ll i=0;i<=1000100;i++)
    {
        if(a[i]!=0)
            hh[cii++]=abs(i-500050);
    }
    int Max=0;
    for(ll i=0;i<cii;i++)
    {
        if(hh[i]>Max)
        {
            Max=hh[i];
        }
        dp[hh[i]]=1;
    }
    int ans=Max+1;
    for(int i=Max;i>=n;i--)
    {
        if(dp[i]==0)
        {
            ans=i;
            continue;
        }
        if(vis[i]==1)
        {
            continue;
        }
        for(int j=2;j*j<=i;j++)
        {
            if(i%j==0)
            {
                vis[j]=1;
                vis[i/j]=1;
                dp[j]=1;
                dp[i/j]=1;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值