HDnoip2017题解

那么,作为一名初入信息竞赛的选手,我也试着开始用博客记录自己的学习历程,那么这篇文章先简单介绍一下我自己吧。

本人开始学习信息学大概以来,主要都是用的C++,所以对其他语言并不是十分熟悉。2016我还只是一名NOIP普及组的选手,水掉一个一等奖后美滋滋继续往下学。最近刚刚搞完今年HDNIOIP提高组前,听同学说最后一道题是省选第二题的难度后我懵逼了(由于最近刚比完如果想要题解可以搜索“xjr01”, hdNOIP2017 题解 -> " http://www.cnblogs.com/xiao-ju-ruo-xjr/ "),其实第一篇文章也不知道写什么,那就胡乱写一下普及组的题解吧(无聊)。

那么首先来看第一题

无脑暴力,哈希前缀和什么的随便写,就不解释了。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 2020
using namespace std;
int n,m,k,a[M],ans,x,y,tmp;
int need(int x){
    if(x%k==0) return x/k;
    return x/k+1;
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        a[x]++;
        a[y]--;
    }
    for(int i=1;i<=n+1;i++){
        tmp+=a[i];
        ans=max(ans,need(tmp));
    }
    ans=min(ans,need(m));
    printf("%d",ans);
    return 0;
}

第二题,一个简单的动态规划

设 f [ i ][ j ][ 0 ]表示时刻 i 时耗费了 j 的体力来到a树,f [ i ][ j ][ 1 ]表示时刻 i 时耗费了 j 的体力来到b树。

转移:

f [ i ][ j ][ 1 ]可以从 f [ i-1 ][  j-2 ][ 0 ]和 f [ i-1 ][ j ][ 1 ]转移

f [ i ][ j ][ 0 ]可以从 f [ i-1 ][  j-1 ][ 1 ]和 f [ i-1 ][ j ][ 0 ]转移

(至于为什么你们自己想)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 300
using namespace std;
int n,m,f[M][M][2],x,a[M],b[M],ans;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&b[i]);
    }
    f[1][0][0]=a[1];
    f[1][0][1]=b[1];
    for(int i=2;i<=n;i++){
        for(int j=0;j<=m;j++){
            f[i][j][0]=f[i-1][j][0];
            f[i][j][1]=f[i-1][j][1];
            if(j>1) f[i][j][1]=max(f[i-1][j-2][0],f[i][j][1]);
            if(j>0) f[i][j][0]=max(f[i-1][j-1][1],f[i][j][0]);
            f[i][j][0]+=a[i];
            f[i][j][1]+=b[i];
        }
    }
    for(int i=0;i<=m;i++){
        ans=max(ans,max(f[n][i][0],f[n][i][1]));
    }
    printf("%d",ans);
    return 0;
}

然后是第三题

第三题大概是比较复杂的一道题了,关键就在于如何处理全排列的序号。
这道题一共分为两部分

第一部分,将给定的全排列转化成全排列的序号。

首先对于一个k,他的全排列一共有k!(k的阶乘)种排列。

设置一个变量 cnt=0,s[n]存储这个排列;

对于第 i 位,若有 k 个 j 满足: i < j <= n 且 s[ j ] > s[ i ],则我们需要将 cnt 加上 ( n - i )!* k。

为什么呢?对于某一个长为 N 的排列 , 一共分为 N 个部分,第 i 个部分是以 第 i 小的数为开头的排列,且这N个部分都有(N-1)!个排列。

也就是说,我们对于每一位,从这一位到结尾都看做一个未离散化的排列(依靠每一个数的大小关系把他们看做一个不是很严谨的排列) ,然后求这个排列在这些数“全排列”中的哪个部分,也就求得了需要从0向后跳个部分才能达到当前的部分。

这样我们就求得了给出序列的序号(编号)

我们将m加上cnt,得到k,就得到了最后应该输出的排列的编号。

第二部分,输出给定编号的全排列。

读到这里,你应该已经明白了,对于第 i 位,我们只要用 k 除以(n-i)的阶乘,就知道这个序列应该是位于第m个部分,然后输出在剩余的未输出过的数中第m小的数即可

所以说,我们只需要一个阶乘的预处理,然后瞎搞就好了。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M
using namespace std;
LL n,m,s[22],p[22];
bool f[22];
LL cnt(LL x){
    LL co=0;
    for(LL i=x+1;i<=n;i++){
        if(p[i]<p[x]) co++;
    }
    return co;
}
LL check(LL x){
    LL co=0;
    for(LL i=1;i<=n;i++){
        if(f[i]) continue;
        co++;
        if(co==x){
            f[i]=true;
            return i;
        }
    }
    return -1;//这句话毫无意义
}
int main(){
    s[0]=1;
    scanf("%lld%lld",&n,&m);
    memset(f,true,sizeof(f));
    for(LL i=1;i<=n;i++) s[i]=s[i-1]*i;
    for(LL i=1;i<=n;i++) scanf("%lld",&p[i]),f[i]=false;
    for(LL i=1;i<=n;i++){
        m+=cnt(i)*s[n-i];
    }
    for(LL i=1;i<=n;i++){
        if(i>1) printf(" ");
        printf("%lld",check(m/s[n-i]+1));
        m%=s[n-i];
    }
    return 0;
}

第四道题,是一个特殊的二叉树,数字由于n<=10,我就只写了一个比较好些的但是比较慢的程序。

这个程序大概是这样,由于在先序遍历中,子节点一定在父节点后才出现,每一个节点的右子节点(如果有的话)总在它父节点的左子节点后出现,所以我只是暴力建树,枚举每个点的父节点,建完树之后用中序遍历检验建的这棵树是否正确即可,如果即可,再直接递归的计算。

然而,万万没想到,我最后还是栽了一个点应为这道题有^的操作(这里的^是指次方,而不是异或),我这里就暂时不写高精度了。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<stack>
#define LL long long
#define M 20
using namespace std;
LL n,m,p[M],f[M],l[M],r[M],c[M],cnt;
string a,b;

bool isd(char x){
    if(x>='0'&&x<='9') return true;
    return false;
}
int take(char x){
    if(isd(x)) return x-'0';
    if(x=='+') return -1;
    if(x=='-') return -2;
    if(x=='*') return -3;
    return -4;
}
bool search(LL x){
    if(p[x]>=0){
        cnt++;
        if(p[x]==c[cnt]) return true;
        return false;
    }
    if(l[x]==0||r[x]==0) return false;
    if(!search(l[x])) return false;
    cnt++;
    if(p[x]!=c[cnt]) return false;
    if(!search(r[x])) return false;
    return true; 
}
LL pow(LL x,LL y){
    if(y==0) return 1;
    return x*pow(x,y-1);
}
LL calc(LL x){
    if(p[x]>=0) return p[x];
    if(p[x]==-1) return calc(l[x])+calc(r[x]);
    if(p[x]==-2) return calc(l[x])-calc(r[x]);
    if(p[x]==-3) return calc(l[x])*calc(r[x]);
    return pow(calc(l[x]),calc(r[x])); 
} 
void dfs(LL x){
    if(x==n+1){
        cnt=0;
        if(search(1)){
            printf("%lld",calc(1));
            exit(0);
        }
        return;
    }
    for(LL i=x-1;i>0;i--){
        if(p[i]>=0) continue;
        if(l[i]==0){
            l[i]=x,f[x]=i;
            dfs(x+1);
            l[i]=0,f[x]=0;
        }
        else if(r[i]==0){
            r[i]=x,f[x]=i;
            dfs(x+1);
            r[i]=0,f[x]=0;
        }
    }
}
int main(){
    scanf("%lld",&n);
    cin>>a;
    cin>>b;
    f[1]=1;
    f[2]=1;
    for(LL i=1;i<=n;i++){
        p[i]=take(a[i-1]);
        c[i]=take(b[i-1]);
    }
    dfs(2);
    return 0;
}

至于提高组的题解,欢迎大家访问http://www.cnblogs.com/xiao-ju-ruo-xjr/ 

原文地址:https://www.cnblogs.com/OYJason/p/7608106.html