题解 | 牛客练习赛87

中位数

是升序的,若,则,答案为
否则,无论如何第小的数都不会是最后得到的数组中第小的数,此时只需把所有操作都加到第个。
时间复杂度

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N=2e5+5;
void ast(ll x,ll l,ll r){assert(x>=l&&x<=r);}
int n,k;
ll a[N];
int main()
{
    int t;scanf("%d",&t);
    ast(t,1,5);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        ast(n,2,200000);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]),ast(a[i],1,200000);
        ast(k,1,n-1);
        sort(a+1,a+1+n);
        for(int i=n-k+1;i<=n;i++) a[n-k]+=a[i];
        n-=k;
        int m=(n+1)/2;
        printf("%lld\n",a[m]);
    }
}

k小数查询

由于的排列,所以只出现一次。
所在的位置为,令中小于的数的个数,中小于的个数。
答案为
用一个桶来计数,枚举即可。
时间复杂度

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,x,k,a[N];
int vis[N];
ll sol(int x,int k)
{
    k--;
    ll ans=0;
    for(int i=1;i<=n;i++)
        if(a[i]==x)
        {
            int r=0;
            for(int j=i+1;j<=n;j++)
            {
                r+=a[j]<x;
                ans+=r==k;
                vis[r]++;
            }
            int l=0;
            for(int j=i-1;j>=1;j--)
            {
                l+=a[j]<x;
                ans+=l==k;
                if(l<=k) ans+=vis[k-l];
            }
        }
    return ans;
}
int main()
{
    scanf("%d%d%d",&n,&x,&k);
    assert(n>=1&&n<=200000);
    assert(x>=1&&x<=n);
    assert(k>=1&&k<=n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),assert(a[i]>=1&&a[i]<=n);
    printf("%lld\n",sol(x,k));
}

牛老板

记忆化搜索,当较小时直接暴力。
比较大时,设为恰好凑出元需要的纸币数量,此时优先用面值为的纸币,因为相对于用面值的纸币,我们省了张纸币,对同理。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
unordered_map<ll,int>f;
vector<ll>v6,v9;
int F(ll n)
{
    if(n==0||f[n]) return f[n];
    f[n]=inf;
    int pos=upper_bound(v6.begin(),v6.end(),n)-v6.begin()-1;
    if(pos>=0)
        f[n]=min(f[n],F(n-v6[pos])+1);
    pos=upper_bound(v9.begin(),v9.end(),n)-v9.begin()-1;
    if(pos>=0)
        f[n]=min(f[n],F(n-v9[pos])+1);
    if(n<=5) f[n]=min(f[n],F(n-1)+1);
    return f[n];
}
int main()
{
    for(ll x=6;x<=1000000000000ll;x*=6) v6.push_back(x);
    for(ll x=9;x<=1000000000000ll;x*=9) v9.push_back(x);
    int t;scanf("%d",&t);
    while(t--)
    {
        ll n;scanf("%lld",&n);
        printf("%d\n",F(n));
    }
}

小G的排列-加强版

可以发现这个条件非常耀眼。
考虑用总排列数减去含有大于等于的连续段的排列的数量。
可以发现长度大于等于的连续段在序列中最多只会出现一次。
为所有长度为的排列中长度为的连续段的出现的总次数,有
考虑一个排列中有一个长度为的连续段,他恰好含有个长度为的连续段,含有个长度为的连续段(比长度为的连续段少一个),因此恰好为长度大于等于的连续段的个数。
因此答案为,预处理阶乘后,可以回答每个查询,时间复杂度

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N=10000005,mod=1e9+7;
ll f[N];
int n,m;
ll sol(int n,int m)
{
    if(m>n) return 0;
    return (n-m+1)*f[n-m+1]%mod*(m>1?2:1)%mod;
}
int main()
{
    f[0]=f[1]=1;
    for(int i=2;i<N;i++) f[i]=f[i-1]*i%mod;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        ll ans=f[n]-sol(n,m+1)+sol(n,m+2);
        ans=(ans%mod+mod)%mod;
        printf("%lld\n",ans);
    }
}

贪吃蛇

条蛇的长度为,不妨蛇
我们看看在一瞬能发生什么: 条蛇被吃掉,剩下条蛇存活。
此时存活这条的蛇由于没蛇可吃,实际上也进入了冷却。
考虑如何吃蛇能让所有蛇变得尽可能的长。
条吃掉第条,随后第条吃掉第条,...,最后第条吃掉第条。
这个时候第一条蛇变为最长,第二条第二长,...,第条第长。
模拟这个策略次即可得出答案。
但是这样复杂度太高,但这个过程可以通过构造一个矩阵来解决:
图片说明
这个矩阵乘法得到的仍然升序。
构造出矩阵后进行矩阵快速幂即可。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N=55,mod=1e9+7,MS=55;
int n,x,M,L[N];
struct Mat
{
    ll a[MS][MS];
    int n,m;
    Mat(int n=0,int m=0):n(n),m(m) {memset(a,0,sizeof(a));}
    Mat operator*(const Mat&B)const
    {
        Mat C(n,B.m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=B.m;j++)
                for(int k=1;k<=m;k++)
                C.a[i][j]=(C.a[i][j]+a[i][k]*B.a[k][j])%mod;
        return C;
    }
};
Mat qpow(Mat a,int n)
{
    Mat ans(a.n,a.n);
    for(int i=1;i<=a.n;i++) ans.a[i][i]=1;
    for(;n;n>>=1,a=a*a)
        if(n&1) ans=ans*a;
    return ans;
}
void ast(int x,int l,int r){assert(x>=l&&x<=r);}
int main()
{
    int t;scanf("%d",&t);
    ast(t,1,50);
    while(t--)
    {
        scanf("%d%d%d",&n,&x,&M);
        ast(n,1,50);
        ast(x,1,1000000);
        ast(M,1,1000000);
        for(int i=1;i<=n;i++) scanf("%d",&L[i]),ast(L[i],1,1000000);
        sort(L+1,L+1+n);
        reverse(L+1,L+1+n);
        M=M/x+1;
        Mat A(n,n);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n-i+1;j++)
            A.a[i][j]=1;
        A=qpow(A,M);
        ll ans=0;
        for(int i=1;i<=n;i++) ans=(ans+1ll*A.a[i][1]*L[i]%mod)%mod;
        printf("%lld\n",ans%mod);
    }
}

简洁的题面

对于一个固定的,考虑这个式子的组合意义:有个木板和种颜色,每个木板可以从种颜色里选一种颜色进行染色,或者不染色,有多少种方案使得每种颜色的出现次数都是的倍数都比较小,于是可以用一个进制来表示染色状态,在各种状态之间建立转移边,再用矩阵快速幂加速计算。
具体的,设为已经对第块木板染色,第种颜色出现的数量可以转移到




压缩成进制状态,在转移之间建立有向边,问题等价于求图中状态到状态长度为的路径的数量。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
int n,k,mod,g;
struct node
{
    ll a[27][27];
    int n;
    node(int n):n(n){memset(a,0,sizeof(a));}
    node operator*(const node&o)const
    {
        node ans(n);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            for(int k=0;k<n;k++)
            ans.a[i][j]=(ans.a[i][j]+a[i][k]*o.a[k][j])%mod;
        return ans;
    }
};
ll qpow(ll a,ll n)
{
    ll ans=1;
    for(;n;n>>=1,a=a*a%mod)
        if(n&1) ans=ans*a%mod;
    return ans;
}
node mqpow(node a,int n)
{
    node ans(a.n);
    for(int i=0;i<a.n;i++) ans.a[i][i]=1;
    for(;n;n>>=1,a=a*a)
        if(n&1) ans=ans*a;
    return ans;
}
int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d",&n,&k,&mod,&g);
        ll ans=0;
        for(int d=1;d<=g;d++)
        {
            if(d==1) ans=(ans+qpow(k+1,n))%mod;
            else
            {
                int st=1;
                for(int i=1;i<=k;i++) st*=d;
                node A(st);
                for(int i=0;i<st;i++)
                {
                    A.a[i][i]=1;
                    for(int j=0,h=1;j<k;j++,h*=d)
                    {
                        int s=i/h%d;
                        int sst=i-s*h;
                        s=(s+1)%d;
                        sst+=s*h;
                        A.a[i][sst]=1;
                    }
                }
                A=mqpow(A,n);
                ans=(ans+A.a[0][0])%mod;
            }
        }
        printf("%lld\n",ans);
    }
}
全部评论

相关推荐

11-01 08:48
门头沟学院 C++
伤心的候选人在吵架:佬你不要的,能不能拿户口本证明过户给我。。球球了
点赞 评论 收藏
分享
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务