2017ACM/ICPC广西邀请赛题解(非官方)

A - A Math Problem

题意:
给定一个正整数 n n n ,计算有多少正整数 k k k 满足 k k ⩽ n k^k\leqslant n kkn 。数据范围1e18。

解法:
先确定上界,然后快速幂暴力即可。

参考代码:

#include <cstdio>
typedef long long ll;
ll qp(int n,int k){
    ll res=1;
    while (k){
        if (k&1) res*=n;
        n*=n;
        k>>=1;
    }
    return res;
}
ll n;
int main(){
//    freopen("in.txt","r",stdin);
    while (~scanf("%lld",&n)){
        if (n>=437893890380859375){
            printf("%d\n",15);
            continue;
        }
        int cnt=0;
        for (int i=1;i<=14;++i){
            if (qp(i,i)<=n) ++cnt;
        }
        printf("%d\n",cnt);
    }
    return 0;
}

D - Covering

题意:
一个 4 × n 4\times n 4×n 的地板,用 2 n 2n 2n 1 × 2 1\times2 1×2 的地毯去覆盖,问有多少覆盖的方式。

解法:
设题中所求为 f ( n ) f(n) f(n) ,递推关系为 f ( n ) = f ( n − 1 ) + 5 f ( n − 2 ) + f ( n − 3 ) − f ( n − 4 ) f(n)=f(n-1)+5f(n-2)+f(n-3)-f(n-4) f(n)=f(n1)+5f(n2)+f(n3)f(n4) 。然后矩阵加快速幂即可。

以下为推导过程:

方法一:
dfs得到前几项,然后项数递增地爆搜找线性递推关系。
方法二:
愣推。考虑前两列,分五种情况,可以参见这篇博客

参考代码:

#include <cstdio>
#include <cstring>
const int MOD=1e9+7;
typedef long long ll;
struct Matrix{
    static const int row=4;
    ll matrix[row][row];
    Matrix(int x=0){
        memset(matrix,0,sizeof(matrix));
        for (int i=0;i<row;++i)
            matrix[i][i]=x;
    }
    Matrix operator*(const Matrix &oth)const{
        Matrix res;
        for (int i=0;i<row;++i){
            for (int j=0;j<row;++j){
                for (int k=0;k<row;++k){
                    res.matrix[i][j]=
                    (res.matrix[i][j]+matrix[i][k]*oth.matrix[k][j]+MOD)%MOD;
                }
            }
        }
        return res;
    }
};
Matrix qp(Matrix n,ll k){
    Matrix res(1);
    while (k){
        if (k&1) res=res*n;
        n=n*n;
        k>>=1;
    }
    return res;
}
ll n;
int solve(){
    if (n<=4){
        if (n==1) return 1;
        else if (n==2) return 5;
        else if (n==3) return 11;
        else return 36;
    }
    n-=4;
    Matrix x;
    x.matrix[0][0]=1;
    x.matrix[0][1]=5;
    x.matrix[0][2]=1;
    x.matrix[0][3]=-1;
    x.matrix[1][0]=1;
    x.matrix[2][1]=1;
    x.matrix[3][2]=1;
    Matrix y=qp(x,n);
    return (y.matrix[0][0]*36%MOD+
            y.matrix[0][1]*11%MOD+
            y.matrix[0][2]*5%MOD+
            y.matrix[0][3]*1%MOD+MOD)%MOD;
}
int main(){
//    freopen("in.txt","r",stdin);
    while (~scanf("%lld",&n)){
        printf("%d\n",solve());
    }
    return 0;
}

E - CS Course

题意:
给定 n n n 个非负整数 a 1 , ⋯ &ThinSpace; , a n a_1,\cdots,a_n a1,,an q q q 个正整数 p p p,问除了 a p a_p ap 之外所有数的且、或、异或运算结果。

解法:
记录 a i a_i ai 之前和之后所有数字的运算结果,则当询问到 a i a_i ai 时将二者再做一次运算即可。

参考代码:

#include <cstdio>
const int MAXN=1e5+10;
int n,q;
int a[MAXN],dp[MAXN][2][3];
int main(){
//    freopen("in.txt","r",stdin);
    while (~scanf("%d%d",&n,&q)){
        for (int i=1;i<=n;++i)
            scanf("%d",&a[i]);
        dp[2][0][0]=dp[2][0][1]=dp[2][0][2]=a[1];
        for (int i=3;i<=n;++i){
            dp[i][0][0]=dp[i-1][0][0]&a[i-1];
            dp[i][0][1]=dp[i-1][0][1]|a[i-1];
            dp[i][0][2]=dp[i-1][0][2]^a[i-1];
        }
        dp[n-1][1][0]=dp[n-1][1][1]=dp[n-1][1][2]=a[n];
        for (int i=n-2;i>=1;--i){
            dp[i][1][0]=dp[i+1][1][0]&a[i+1];
            dp[i][1][1]=dp[i+1][1][1]|a[i+1];
            dp[i][1][2]=dp[i+1][1][2]^a[i+1];
        }
        for (int i=0;i<q;++i){
            int p;
            scanf("%d",&p);
            if (p==1){
                printf("%d %d %d\n",dp[1][1][0],dp[1][1][1],dp[1][1][2]);
            }
            else if (p==n){
                printf("%d %d %d\n",dp[n][0][0],dp[n][0][1],dp[n][0][2]);
            }
            else{
                int _and=dp[p][0][0]&dp[p][1][0];
                int _or=dp[p][0][1]|dp[p][1][1];
                int _xor=dp[p][0][2]^dp[p][1][2];
                printf("%d %d %d\n",_and,_or,_xor);
            }
        }
    }
    return 0;
}

G - Duizi and Shunzi

题意:
给定 n n n 个数 a 1 ⋯ &ThinSpace; , a n a_1\cdots,a_n a1,an ,定义两个相同的数字为一个“对子”,三个连续的数字为一个“顺子”。问用这 n n n 个数最多能组成多少个对子加顺子(使和值达到最大,一个数字只能用一次,可以不用光)。

解法:
思路:贪心,用最少的牌组成尽可能多的对子或顺子。
由于组成“顺子”需要三张,而组成“对子”只需要两张,因此优先组成对子。但也有特例除外,即sample中1 2 3 3 4 5的情况。这是因为,按照优先规则,3前面的1和2没有被使用,浪费了。如果当前考虑的这张牌能利用起它前面的两张牌,则等于是用1张就组成了一个东西,是比组对子更优的。
因此最终解法为,从小到大排好序,从前向后看,当前牌如果能和前面两张牌组成顺子,则组顺子,然后用剩下的组对子;否则全部用来组对子。

参考代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define max std::max
#define min std::min
const int MAXN=1e6+10;
int a[MAXN];
int n,lo,hi;
bool shunzi(int x){
    return x>=lo&&x+2<=hi&&a[x]&&a[x+1]&&a[x+2];
}
int solve(){
    int res=0;
    for (int i=lo;i<=hi;++i){
        if (shunzi(i-2)){
            a[i-2]--;
            a[i-1]--;
            a[i]--;
            res++;
        }
        res+=a[i]>>1;
        a[i]=(bool)(a[i]&1);
    }
    return res;
}
int main(){
//    freopen("in.txt","r",stdin);
    while (~scanf("%d",&n)){
        memset(a,0,sizeof(a));
        lo=MAXN,hi=0;
        for (int i=0;i<n;++i){
            int x;
            scanf("%d",&x);
            hi=max(hi,x);
            lo=min(lo,x);
            a[x]++;
        }
        printf("%d\n",solve());
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值