2020牛客暑假多校训练营 (第六场) B,C,E,G,H,K

B Binary Vector


可以直接通过样例找规律推递推式,其证明如下。
在这里插入图片描述

#include <iostream>
#include <stdio.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=2e7+5;
int T,n;
long long f[maxn],Xor[maxn];
ll quickpow(ll x,ll y)
{
    ll sum=1;
    while(y)
    {
        if(y&1)
        sum=(sum*x)%mod;
        y>>=1;
        x=(x*x)%mod;
    }
    return sum;
}
int main()
{
    ll q=quickpow(2,mod-2);
    ll now=2;
    ll inv=q;
    f[0]=1;
    for(int i=1;i<=2e7;++i)
    {
        f[i]=((f[i-1]*(now-1)%mod)*inv)%mod;
        now=now*2%mod;
        inv=inv*q%mod;
        Xor[i]=Xor[i-1]^f[i];
    }
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        printf("%lld\n",Xor[n]);
    }
    return 0;
}

C Combination of Physics and Math


这题就是要证以下结论。
在这里插入图片描述
然后我们比较所有单列的情况,取最大值。

#include <bits/stdc++.h>
using namespace std;
const int maxn=205;
const double eps=1e-8;
int T;
int n,m,k;
double a[maxn];
double sum[maxn];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        double ma=0.0;
        scanf("%d %d",&n,&m);
        for(int i=1;i<=m;++i)
        sum[i]=0.0;
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                scanf("%lf",&a[j]);
                sum[j]+=a[j];
                ma=max(ma,sum[j]/a[j]);
            }
        }
        printf("%.8lf\n",ma);
    }
    return 0;
}

E Easy Construction


本题,也是通过几组数据找到规律。我们发现长度为n的子序列就是P本身,因此其加和必须满足 n*(n+1)/2%n=k。我们分奇数偶数讨论。当n为奇数时,n+1为偶数,那么 k=0 才能使等式成立。当n为偶数时,当 k=n/2 才能使等式成立。当然,现在只是必要条件,我们先找构造。当n为偶数,其构造P为{n,n/2,n-1,1…}。当n为奇数,其构造P为{n,n-1,1…}。我们可以发现,只要构造成这样,那么每种长度的情况都能一个子序列之和%n=k,这个与一个数肯定能被2 n相加表示一样。那么找到构造的话,就是充要条件了。

#include <iostream>
#include <stdio.h>
using namespace std;
int n,k;
int main()
{
    scanf("%d %d",&n,&k);
    if(n&1)
    {
        if(k)
        {
            puts("-1");
        }
        else
        {
            printf("%d",n);
            int cnt=0;
            for(int i=2;i<=n;++i)
            {
                if(i%2==0)
                {
                    ++cnt;
                    printf(" %d",n-cnt);
                }
                else
                printf(" %d",cnt);
            }
        }
    }
    else
    {
        if(k!=n/2)
        {
            puts("-1");
        }
        else
        {
            printf("%d %d",n,n/2);
            int cnt=0;
            for(int i=3;i<=n;++i)
            {
                if(i&1)
                {
                    ++cnt;
                    printf(" %d",n-cnt);
                }
                else
                {
                    printf(" %d",cnt);
                }
            }
        }
    }
    return 0;
}

G Grid Coloring


一个构造题,这里只给出一种构造方法,首先,buzhengchun=1,k=1,k不整除2n(n+1),是没有方案的。然后,如果k不整除n,那么直接顺序放就可以了,这样相邻的横边和纵边肯定颜色不同,并且因为k不整除n,那么1*1的环内就不会是相同颜色。如果k|n,那么顺序放的话,1*1的环内就会有相同颜色,我们只需要在偶数行或列,将防止顺序移动一位即可。

#include <iostream>
#include <stdio.h>
using namespace std;
int T,n,k;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&k);
        if(n==1||k==1||2*n*(n+1)%k>0)
        {
            puts("-1");
            continue;
        }
        if(n%k==0)
        {
            int c=0;
            for(int i=1;i<=2*(n+1);++i)
            {
                int last=c;
                for(int j=1;j<=n;++j)
                {
                    last=last%k+1;
                    if(j!=n)
                    printf("%d ",last);
                    else
                    printf("%d\n",last);
                }
                c^=1;
            }
        }
        else
        {
            int last=0;
            for(int i=1;i<=2*(n+1);++i)
            {
                for(int j=1;j<=n;++j)
                {
                    last=last%k+1;
                    if(j!=n)
                    printf("%d ",last);
                    else
                    printf("%d\n",last);
                }
            }
        }
    }
    return 0;
}

H Harmony Pairs


数位dp,dp[pos][diff][lim1][lim2]代表A和B在已找到pos位,diff代表S(A)-S(B),可能为负数,因此要+1000,lim1代表当前B是否等于N,lim2代表当前A是否等于B。

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
const int maxn=105;
const long long mod=1e9+7;
long long dp[maxn][2001][2][2];
char str[maxn];
int a[maxn];
long long dfs(int pos,int diff,int lim1,int lim2)
{
    if(!pos)
    {
        return 1ll*(diff>1000);
    }
    if(dp[pos][diff][lim1][lim2]!=-1)
    return dp[pos][diff][lim1][lim2];
    int up1=lim1?a[pos]:9;
    long long ans=0;
    for(int i=0;i<=up1;++i)//B
    {
        int up2=lim2?i:9;
        for(int j=0;j<=up2;++j)
        {
            ans+=dfs(pos-1,diff+j-i,lim1&(i==a[pos]),lim2&(i==j));
            ans%=mod;
        }
    }
    dp[pos][diff][lim1][lim2]=ans;
    return ans;
}
void init()
{
    memset(dp,-1,sizeof(dp));
}
int main()
{
    init();
    scanf("%s",str+1);
    int len=strlen(str+1);
    for(int i=1;i<=len;++i)
    a[len-i+1]=str[i]-'0';
    printf("%lld\n",dfs(len,1000,1,1));
    return 0;
}

K K-Bag


我们可以发现,如果有两个数相等,且他们之间的距离小于k,那么这两个数间的所有数,都可能作为后一个数所在序列的开头,因此,这些元素权值+1,并且此时限制数+1,如果一个数作为某个序列的开头,那么其%k之后的位置,便是第一个完整序列的开头,那么将所有位置的权值加到其%k之后的位置,如果能在前1~k个位置,找到与限制数相等的权值(意味着以此为第一序列开头,满足之后所有限制),那么就是k-bag,否则就不是。
但是,如果每次直接暴力权值+1,会导致复杂度太大,我们可以利用差分,例如区间[l,r]元素+1,那么差分之后,就是l所在位置+1,r+1所在位置-1。这样每次就是O(1)的复杂度了。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int T,n,k;
int a[maxn],vis[maxn],num[maxn];
map<int,int>mp;
int cnt;
void init(int n)
{
    cnt=0;
    mp.clear();
    for(int i=1;i<=n;++i)
    num[i]=vis[i]=0;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&k);
        init(n);
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&a[i]);
            if(mp[a[i]])
            {
                if(i-mp[a[i]]<k)
                {
                    cnt++;
                    vis[mp[a[i]]+1]++;
                    vis[i+1]--;
                }
            }
            mp[a[i]]=i;
        }
        int pre=0;
        bool flag=0;
        for(int i=1;i<=n;++i)
        {
            pre+=vis[i];
            num[i%k+1]+=pre;
        }
        for(int i=1;i<=n;++i)
        {
            if(num[i]==cnt)
            {
                flag=true;
                break;
            }
        }
        if(flag)
        puts("YES");
        else
        puts("NO");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值