2017/11/9模拟赛总结

来源:互联网 发布:axure7.0mac中文版下载 编辑:程序博客网 时间:2024/06/06 11:02

NOIP前最后一场模拟赛

T1 K值查询

二分答案
关键是如何check

解法1

假设当前二分答案的区间为[1,x]
即求在[1,x]内有恰好k个数的最小的x
考虑枚举两数相乘中较小的那个数
nm上都枚举一遍
而中间有一部分计算重复了 减去即可
O(nlogn)

#include<bits/stdc++.h>using namespace std;int n,m; long long check(int x){    int i,t=sqrt(x);    long long sum=0;    for (i=1;i<=min(t,n);i++) sum=sum+x/i;    for (i=1;i<=min(t,m);i++) sum=sum+x/i;    return sum-t*t;}int main(){    int q;    scanf("%d%d%d",&n,&m,&q);    while (q--){        int x;        scanf("%d",&x);        int l=1,r=x,res=0;        while (l<=r){            int mid=(l+r)>>1;            if (check(mid)>=x) res=mid,r=mid-1;            else l=mid+1;        }        printf("%d\n",res);    }    return 0;}

解法2

其实[1,x]内的数为i=1mni(nm的位置可以调换)
ni的值是单调的 而且只有2n个取值
那么可以用简单的除法找到每个取值的区间 然后求和即可
O(nlogn)

#include<bits/stdc++.h>using namespace std;int n,m; long long check(int x){    int i,j;    long long sum=0;    for (i=m;i;i=j){        j=x/(x/i+1);        sum+=x/i*(i-j);    }       return sum;}int main(){    int q;    scanf("%d%d%d",&n,&m,&q);    while (q--){        int x;        scanf("%d",&x);        int l=1,r=x,res=0;        while (l<=r){            int mid=(l+r)>>1;            if (check(mid)>=x) res=mid,r=mid-1;            else l=mid+1;        }        printf("%d\n",res);    }    return 0;}


T2 成绩比较

看起来就像是dp+组合数 但是实现起来并不简单
dpi,j表示前i个科目 有j个人已经确定不被碾压(至少有一门大于B) 的方案数
dpi,j=k=max(0,j(ri1))j(dpi1,kCjkn1kCri1(jk)k)
前面的那个组合数是在剩下没有确定的人中选出jk个人
后面的那个组合数是把这门功课剩下比B高的位置分给前面的人(为什么不分给后面才确定的人?因为分给后面的人就会导致确定不被碾压的人更多 和dp状态不符)

人的方案解决了 还需要乘上每门科目所有人分数的方案数
可以枚举B的分数x 那么方案数就为x=1uixnri(uix)ri1
但是ui达到了109 这样是不现实的
换一个思路 考虑枚举比B大的人 和小于等于B的人不同分数的个数(设为ab)
那么贡献为Ca+bui(fnri,b1+fnri,b)fri1,a
其中fi,j表示i个人中 分到j种分数 的方案数
也就是有序的第二类斯特林数 可以用O(n2)递推得到

这样答案乘上所有方案数即可
O(n3)

#include<bits/stdc++.h>using namespace std;#define N 110#define mo 1000000007int n,m,K,u[N],r[N];int C[N][N],dp[N][N],S[N][N],c[N],Inv[N];int Pow(int A,int B){    int res=1;    while (B){        if (B&1) res=1LL*res*A%mo;        B>>=1;        A=1LL*A*A%mo;    }    return res;}void init(){    int i,j;    for (i=0;i<=n;i++){        C[i][0]=1;        for (j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;    }    S[0][0]=1;    for (i=1;i<=n;i++)        for (j=1;j<=i;j++)            S[i][j]=1LL*(S[i-1][j-1]+S[i-1][j])*j%mo;    for (i=1;i<=n;i++) Inv[i]=Pow(i,mo-2);}void solve(){    int i,j,k;    init();    dp[0][0]=1;    for (i=1;i<=m;i++)        for (j=0;j<=n-K-1;j++)            for (k=max(0,j-(r[i]-1));k<=j;k++)                dp[i][j]=(dp[i][j]+1LL*dp[i-1][k]*C[n-1-k][j-k]%mo*C[k][r[i]-1-(j-k)]%mo)%mo;    int sum=dp[m][n-K-1];    for (i=1;i<=m;i++){        int tmp=0;        c[0]=1;        for (j=1;j<=n;j++) c[j]=1LL*c[j-1]*(u[i]-j+1)%mo*Inv[j]%mo;        for (j=0;j<r[i];j++)            for (k=1;k<=n-r[i]+1;k++)                tmp=(tmp+1LL*(S[n-r[i]][k]+S[n-r[i]][k-1])*S[r[i]-1][j]%mo*c[j+k])%mo;        sum=1LL*sum*tmp%mo;    }    printf("%d\n",sum);}int main(){    int i;    scanf("%d%d%d",&n,&m,&K);    for (i=1;i<=m;i++) scanf("%d",&u[i]);    for (i=1;i<=m;i++) scanf("%d",&r[i]);    solve();    return 0;}


T3 数塔

只需要正反各推一遍 得到某个点向上/向下的最大路径
然后对于每一行累计一下前缀/后缀最大值 就可以O(1)查询了
O(n2+m)

#include<bits/stdc++.h>using namespace std;#define N 1010#define M 500010int a[N][N],n,m,dp1[N][N],dp2[N][N],suml[N][N],sumr[N][N];int main(){    int i,j;    scanf("%d%d",&n,&m);    for (i=1;i<=n;i++)        for (j=1;j<=i;j++)            scanf("%d",&a[i][j]);    dp1[1][1]=a[1][1];    for (i=2;i<=n;i++)        for (j=1;j<=i;j++)            dp1[i][j]=max(dp1[i-1][j-1],dp1[i-1][j])+a[i][j];    for (i=1;i<=n;i++) dp2[n][i]=a[n][i];    for (i=n-1;i>=1;i--)        for (j=1;j<=i;j++)            dp2[i][j]=max(dp2[i+1][j+1],dp2[i+1][j])+a[i][j];    for (i=1;i<=n;i++){        for (j=1;j<=i;j++) suml[i][j]=max(suml[i][j-1],dp1[i][j]+dp2[i][j]-a[i][j]);        for (j=i;j>=1;j--) sumr[i][j]=max(sumr[i][j+1],dp1[i][j]+dp2[i][j]-a[i][j]);    }    for (i=1;i<=m;i++){        int x,y;        scanf("%d%d",&x,&y);        if (x==1 && y==1){            printf("-1\n");            continue;        }        printf("%d\n",max(suml[x][y-1],sumr[x][y+1]));    }    return 0;}


NOIP2017倒计时2天
如果遇到第二题很难的话应该及时跳过 而不是死磕
做题策略还是很重要的

Date:2017/11/9
By CalvinJin

原创粉丝点击