2018NOIP提高组模拟1

版权声明:小蒟蒻的博客转载也请注明出处哦 https://blog.csdn.net/qq_42835823/article/details/81869085

2018NOIP提高组模拟1

———————————————————————————————20180819

1·Number

题目描述

如果一个数能够表示成两两不同的 3 的幂次的和,就说这个数是好的。
比如 13 是好的,因为 13 = 9 + 3 + 1 。
又比如 90 是好的,因为 90 = 81 + 9 。
现在我们用 a[i] 表示第 i 小的好数。
比如 a[1] = 1, a[2] = 3, a[5] = 10 。
给定 L,R,请求出 a[L] 到 a[R] 的 和 mod 232。

输入格式

第一行一个整数 T,表示数据组数。
接下来 T 行,每行两个整数 L,R 表示一组询问。

输出格式

输出 T 行,每行为一个整数,表示相应询问的答案。
整数,表示相应询问的答案。

输入样例

5
1 3
3 3
4 5
6 7
2 5

输出样例

8
4
19
25
26

备注

【数据范围】
对 30% 的输入数据:1≤T≤100;R≤1000 。
对 100% 的输入数据:1≤T≤100000;1≤L≤R≤10^18。

题解

如果用三进制表示好数,那必定是一个01串,这让人很容易联想到二进制。
所以我们可以用十进制转二进制再用三进制算。
例如19的二进制为10011,当三进制算出第19大的好数为81+3+1=85。

我们可以用二进制准确寻找到第i小的好数。
再统计a[1]到a[R]的和 和 a[1]到a[L]的和。
然后我们就可以用类似数位DP的方法, 确定每一位对于答案的贡献,累加这个值。

注意中间过程会超过long long 的范围

#include<iostream>
#include<cstdio>
using namespace std;
#define ULL unsigned long long
void read(ULL &x){
    x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';
        c=getchar();
    }
}
ULL mod=1LL<<32LL,e,f,t;
ULL ask(ULL x){
    ULL pre=0,ans=0,c2=1,c3=1,sum=0;
    for(int step=0;x;x>>=1,step++){
        if(x&1){
            ans=(ans+pre*c3)%mod;
            ans=(ans+sum*c2)%mod;
        }
        if(step)c2=c2*2%mod;//
        pre=(pre+(x&1)*c2)%mod;
        sum=(sum+c3)%mod;
        c3=c3*3%mod;
    }
    return ans;
}
int main(){
    read(t);
    while(t--){
        read(e);read(f);
        printf("%I64d\n",(ask(f+1)-ask(e)+mod)%mod);//
    }
    return 0;
}

2·Dp

题目描述

一块土地有 n 个连续的部分,用 H[1],H[2],…,H[n] 表示每个部分的最初高度。有 n 种泥土可用,他们都能覆盖连续的 k 个部分,第 i 种泥土的价格为 C[i],可以使 i,i+1,…,i+k-1 部分的高度增加 E[i](如果 i+k>n,那就覆盖 i,…,n ),我们必须满足以下条件:
1、每种泥土只能使用一次。
2、成本必须小于等于 m 。
要求在上述条件下,使得最低的部分的高度尽量高,请求出这个高度。

输入格式

第一行三个整数 n,m,k,表示土地有几个部分,最大预算成本以及每种泥土能覆盖的部分数。
接下来 n 行,每行三个整数 H[i],E[i],C[i]。

输出格式

输出一个整数,表示满足条件的情况下,最低部分的高度的最大值。

样例输入

4 20 1
1 3 5
1 7 3
4 6 9
3 5 13

样例输出

3

备注

【数据范围】
对 30% 的输入数据:1≤n≤20 。
对 100% 的输入数据:1≤k≤11;1≤n≤100;0≤m;H[i],E[i],C[i]≤10^6 。

题解

1 二分加贪心(预计70分)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define inf 0x7fffffff
int n,m,k,h[110],e[110],c[110],a[110],b[110],cost;
bool check(int x){
    memcpy(a,h,sizeof(h));
    memset(b,0,sizeof(b));
    cost=0;
    for(int i=1;i<=n;i++){
        if(a[i]<x){
            cost+=c[i];
            if(cost>m)return false;
            for(int j=i;j<i+k;j++)
                a[j]+=e[i];
            if(a[i]<x){
                for(int j=i-k+1;j<i;j++){
                    if(b[j]){
                        b[j]=0;
                        for(int l=i;l<j+k;l++)
                            a[l]+=e[j];
                        if(a[i]>=x)continue;
                    }
                }
            }
            if(a[i]<x)return 0;
        }
        else b[i]=1;
    }
    return 1;
}
int erfen(){
    int l=0,r=inf;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(check(mid))l=mid;
        else r=mid;
    }
    if(check(r))return r;
    return l;
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&h[i],&e[i],&c[i]);
    printf("%d",erfen());
    return 0;
}
正解(预计100分)

最小的最大——二分的明显标志。
二分最少用多少钱。

检验时用f[i][j]表示前i个部分满足要,且j为第i –k + 1 到第i 中泥土的选取情况下
(j为二进制,j<2^11[2048]) 最少用了多少钱。
每次向右移动一个单位的土地,相当于第i –k + 1块土地不考虑,
转而考虑第i + 1块土地,枚举所以可能的状态。
所以复杂度为O (n * 2^k * logX)。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define inf 0x3f3f3f3f
int n,m,k,h[110],e[110],c[110],f[101][2500];
bool check(int x){
    memset(f,0x3f,sizeof(f));
    f[0][0]=0;
    //枚举所有状态 
    for(int i=1;i<=n;i++)
        for(int j=0;j<1<<k;j++)
            if(f[i-1][j]<inf){
                int cnt=h[i];
                for(int l=1;l<k;l++)
                    if(j&(1<<(l-1)))cnt+=e[i-l];//算之前加的 
                if(cnt>=x)
                    if(j&(1<<(k-1))){
                        if(f[i-1][j]<f[i][(j^(1<<(k-1)))<<1])
                            f[i][(j^(1<<(k-1)))<<1]=f[i-1][j];
                    }
                    else{
                        if(f[i-1][j]<f[i][j<<1])
                            f[i][j<<1]=f[i-1][j];
                    }
                //转移状态 
                if(cnt+e[i]>=x)
                    if(j&(1<<(k-1))){
                if(f[i-1][j]+c[i]<f[i][((j^(1<<(k-1)))<<1)+1])
                    f[i][((j^(1<<(k-1)))<<1)+1]=f[i-1][j]+c[i];
                    }
                    else{
                        if(f[i-1][j]+c[i]<f[i][(j<<1)+1])
                            f[i][(j<<1)+1]=f[i-1][j]+c[i];
                    }
            }
    int ans=inf;
    for(int j=0;j<1<<k;j++)
        if(f[n][j]<ans)ans=f[n][j];
    return (ans<=m);
}
int erfen(){
    int l=0,r=inf;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(check(mid))l=mid;
        else r=mid;
    }
    if(check(r))return r;
    return l;
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&h[i],&e[i],&c[i]);
    printf("%d",erfen());
    return 0;
}

3·Change

题目描述

Alice 和 Bob 又聚在一起了!他们已经厌倦了取石子游戏,现在他们热衷于切题。于是,Alice 找到了一道题让 Bob 做。Alice 有一张 N*M 的表格,每个格子上有一个值 a[i][j] (1≤i≤N,1≤j≤ M),Alice 将会给 Bob 若干个操作,操作分以下三类:

交换两行
交换两列
输出某一个格子上的值
由于 Bob 正在为给 Alice 出题而发愁,他请你完成这个题。

输入格式

第一行包含三个整数 N,M,Q,表示表格有 N 行 M 列,以及有 Q 个操作。
接下来 N 行,每行 M 个数用来描述 Alice 的表格。
接下来 Q 行,每行一个字符 S 和两个整数 x,y。其中 S 取 c,r,g 中的一个。
如果 S=c ,交换 x,y两列(1≤x,y≤m);
如果 S=r ,交换 x,y两行(1≤x,y≤n);
如果 S=g ,输出 a[x][y](1≤x≤n;1≤y≤m)。

输出格式

对于每一个 S=g 的操作,输出要求的数并换行。

样例数据 1

输入

3 3 5
1 2 3
4 5 6
7 8 9
g 3 2
r 3 2
c 2 3
g 2 2
g 3 2

输出

8
9
6

备注

【数据范围】
对 50% 的输入数据 :1≤n,m,Q≤100。
对 100% 的输入数据 :1≤n,m≤1000;1≤Q≤500000;a[i][j]≤10^6。

题解

我们不难发现交换行的时候对列的情况实质没有影响,
交换列的时候对行的情况实质没有影响;

所以我们只要用两个数组维护在每步操作后,
现在的第i行是原来的第几行,现在的第j列是原来 的第几列就行了;

#include<iostream>
#include<cstdio>
using namespace std;
void read(int &x){
    x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';
        c=getchar();
    }
}
int n,m,q,a[1001][1001],idx[1001],idy[1001];
int main(){
    read(n);read(m);read(q);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            read(a[i][j]);
    for(int i=1;i<=1000;i++){
        idx[i]=i;idy[i]=i;
    }
    while(q--){
        char c=1;int e,f;
        while(c<'a'||c>'z')c=getchar();
        read(e);read(f);
        if(c=='g'){
            printf("%d\n",a[idx[e]][idy[f]]);
            continue;
        }
        if(c=='c'){
            swap(idy[e],idy[f]);
            continue;
        }
        if(c=='r')swap(idx[e],idx[f]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42835823/article/details/81869085