那么,作为一名初入信息竞赛的选手,我也试着开始用博客记录自己的学习历程,那么这篇文章先简单介绍一下我自己吧。
本人开始学习信息学大概以来,主要都是用的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/