首页 > 代码库 > 9.2noip模拟试题
9.2noip模拟试题
题目名称 | 改造二叉树 | 数字对 | 交换 |
英文名称 | binary | pair | swap |
输入文件名 | binary.in | pair.in | swap.in |
输出文件名 | binary.out | pair.out | swap.out |
时间限制 | 1s | 2s | 1s |
空间限制 | 256M | 256M | 256M |
测试点数目 | 20 | 20 | 10 |
测试点分值 | 5 | 5 | 10 |
是否有部分分 | 无 | 无 | 无 |
题目类型 | 传统 | 传统 | 传统 |
是否有SPJ | 无 | 无 | 无 |
1.改造二叉树
【题目描述】
小Y在学树论时看到了有关二叉树的介绍:在计算机科学中,二叉树是每个结点最多有两个子结点的有序树。通常子结点被称作“左孩子”和“右孩子”。二叉树被用作二叉搜索树和二叉堆。随后他又和他人讨论起了二叉搜索树。
什么是二叉搜索树呢?二叉搜索树首先是一棵二叉树。设key[p]表示结点p上的数值。对于其中的每个结点p,若其存在左孩子lch,则key[p]>key[lch];若其存在右孩子rch,则key[p]<key[rch];注意,本题中的二叉搜索树应满足对于所有结点,其左子树中的key小于当前结点的key,其右子树中的key大于当前结点的key。
小Y与他人讨论的内容则是,现在给定一棵二叉树,可以任意修改结点的数值。修改一个结点的数值算作一次修改,且这个结点不能再被修改。若要将其变成一棵二叉搜索树,且任意时刻结点的数值必须是整数(可以是负整数或0),所要的最少修改次数。
相信这一定难不倒你!请帮助小Y解决这个问题吧。
【输入格式】
第一行一个正整数n表示二叉树结点数。结点从1~n进行编号。
第二行n个正整数用空格分隔开,第i个数ai表示结点i的原始数值。
此后n - 1行每行两个非负整数fa, ch,第i + 2行描述结点i + 1的父亲编号fa,以及父子关系ch,(ch= 0 表示i + 1为左儿子,ch = 1表示i+ 1为右儿子)。
结点1一定是二叉树的根。
【输出格式】
仅一行包含一个整数,表示最少的修改次数。
【样例输入】
3
2 2 2
1 0
1 1
【样例输出】
2
【数据范围】
20 % :n <= 10 , ai <= 100.
40 % :n <= 100 , ai <= 200
60 % :n <= 2000 .
100 % :n <= 10 ^ 5 , ai < 2 ^31.
/*想到了中序遍历然后做lis 但是只25分 有一种情况是2 5 3 4这样lis是3 按说改一个5就行但是题目限制了改成整数 2 3之间不能填了这就wawawawawa~这里学习了一种常见的将严格上升的的序列等价转化成不降的序列的方法 a1 a2 a3 -> a1-1 a2-2 a3-3这样再做不降的lis就好了 */#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>#define maxn 200010using namespace std;int n,a[maxn],b[maxn],len,lc[maxn],rc[maxn],c[maxn],r;void Dfs(int x){ if(lc[x])Dfs(lc[x]); b[++len]=x; if(rc[x])Dfs(rc[x]);}void LIS(){ for(int i=1;i<=len;i++){ int x=a[b[i]];x-=i; if(x>=c[r]){ c[++r]=x; continue; } int p=upper_bound(c+1,c+1+r,x)-c; c[p]=x; }} int main(){ freopen("binary.in","r",stdin); freopen("binary.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int x,y; for(int i=2;i<=n;i++){ scanf("%d%d",&x,&y); if(y==0)lc[x]=i; if(y==1)rc[x]=i; } Dfs(1); LIS(); printf("%d\n",len-r); return 0;}
2.数字对
【题目描述】
小H是个善于思考的学生,现在她又在思考一个有关序列的问题。
她的面前浮现出一个长度为n的序列{ai},她想找出一段区间[L, R](1 <= L <= R <= n)。
这个特殊区间满足,存在一个k(L <= k <= R),并且对于任意的i(L <= i <= R),ai都能被ak整除。这样的一个特殊区间 [L, R]价值为R - L。
小H想知道序列中所有特殊区间的最大价值是多少,而有多少个这样的区间呢?这些区间又分别是哪些呢?你能帮助她吧。
【输入格式】
第一行,一个整数n.
第二行,n个整数,代表ai.
【输出格式】
第一行两个整数,num和val,表示价值最大的特殊区间的个数以及最大价值。
第二行num个整数,按升序输出每个价值最大的特殊区间的L.
【样例输入1】
5
4 6 9 3 6
【样例输出1】
1 3
2
【样例输入2】
5
2 3 5 7 11
【样例输出2】
5 0
1 2 3 4 5
【数据范围】
30%:1 <= n <= 30 , 1 <= ai <= 32.
60%:1 <= n <= 3000 , 1 <= ai <= 1024.
80%:1 <= n <= 300000 , 1 <= ai <= 1048576.
100%: 1 <= n <= 500000 , 1 <= ai < 2 ^ 31.
暴力:
/*n*n暴力 枚举ak 然后左右拓展虽然是暴力 但是跑的飞快~~注意输出的是不一样的区间.... */#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define maxn 500010using namespace std;int n,a[maxn],mxx,mxl,sum,Ans[maxn];struct node{ int len,L;}ans[maxn];int init(){ int x=0;char s=getchar(); while(s<‘0‘||s>‘9‘)s=getchar(); while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();} return x;}int cmp(const node &x,const node &y){ if(x.len==y.len)return x.L<y.L; return x.len>y.len;}int Gcd(int a,int b){ return !b?a:Gcd(b,a%b);}int main(){ freopen("pair.in","r",stdin); freopen("pair.out","w",stdout); n=init(); for(int i=1;i<=n;i++) a[i]=init(); for(int k=1;k<=n;k++){ int li=k,ri=k,gcd=a[k]; for(int i=k+1;i<=n;i++) if(Gcd(gcd,a[i])!=gcd)break; else ri++; for(int i=k-1;i>=1;i--) if(Gcd(gcd,a[i])!=gcd)break; else li--; ans[k].len=ri-li;ans[k].L=li; } sort(ans+1,ans+1+n,cmp); mxx=ans[1].len;mxl=ans[1].L; Ans[++sum]=mxl; for(int k=2;k<=n;k++) if(ans[k].len!=mxx)break; else if(ans[k].L!=mxl){ Ans[++sum]=ans[k].L; mxl=ans[k].L; } printf("%d %d\n",sum,mxx); for(int i=1;i<=sum;i++) printf("%d ",Ans[i]); return 0;}
ST表:
/*nlogn做法同样的枚举ak 左右拓展的时候二分来搞这里恰好有单调性~ST表预处理一下 O(1)查询 */#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define maxn 500010using namespace std;int n,a[maxn],f[maxn][20],p[maxn],mxx,mxl,sum,Ans[maxn];struct node{ int len,L;}ans[maxn];int init(){ int x=0;char s=getchar(); while(s<‘0‘||s>‘9‘)s=getchar(); while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();} return x;}int cmp(const node &x,const node &y){ if(x.len==y.len)return x.L<y.L; return x.len>y.len;}int Gcd(int a,int b){ return !b?a:Gcd(b,a%b);}int Get_pow(int x){ for(int i=0;;i++) if((1<<i)>x)return i-1;}void Get_ST(){ for(int i=1;i<=n;i++) f[i][0]=a[i]; for(int j=1;j<=18;j++) for(int i=1;i+(1<<j)-1<=n;i++) f[i][j]=Gcd(f[i][j-1],f[i+(1<<(j-1))][j-1]); for(int i=1;i<=n;i++) p[i]=Get_pow(i);}int find(int l,int r){ if(l>r)return 0; int len=p[r-l+1]; return Gcd(f[l][len],f[r-(1<<len)+1][len]);}int main(){ freopen("pair.in","r",stdin); freopen("pair.out","w",stdout); n=init(); for(int i=1;i<=n;i++) a[i]=init(); Get_ST(); for(int k=1;k<=n;k++){ int li=k,ri=k,gcd=a[k],l,r; l=0;r=n-k+1; while(l<=r){ int mid=l+r>>1; int x=find(k,k+mid-1); if(x==gcd){ ri=k+mid-1;l=mid+1; } else r=mid-1; } l=0;r=k; while(l<=r){ int mid=l+r>>1; int x=find(k-mid+1,k); if(x==gcd){ li=k-mid+1;l=mid+1; } else r=mid-1; } ans[k].len=ri-li;ans[k].L=li; } sort(ans+1,ans+1+n,cmp); mxx=ans[1].len;mxl=ans[1].L; Ans[++sum]=mxl; for(int k=2;k<=n;k++) if(ans[k].len!=mxx)break; else if(ans[k].L!=mxl){ Ans[++sum]=ans[k].L; mxl=ans[k].L; } printf("%d %d\n",sum,mxx); for(int i=1;i<=sum;i++) printf("%d ",Ans[i]); return 0;}
3.交换
【题目描述】
给定一个{0, 1, 2, 3, … , n - 1}的排列 p。一个{0, 1, 2 , … , n - 2}的排列q被认为是优美的排列,当且仅当q满足下列条件:
对排列s = {0, 1, 2, 3, ..., n - 1}进行n – 1次交换。
- 交换s[q0],s[q0 + 1]
- 交换s[q1],s[q1 + 1]
…
最后能使得排列s = p.
问有多少个优美的排列,答案对10^9+7取模。
【输入格式】
第一行一个正整数n.
第二行n个整数代表排列p.
【输出格式】
仅一行表示答案。
【样例输入】
3
1 2 0
【样例输出】
1
【样例解释】
q = {0,1}{0,1,2} ->{1,0,2} -> {1, 2, 0}
q = {1,0}{0,1,2} ->{0,2,1} -> {2, 0, 1}
【数据范围】
30%: n <= 10
100%: n <= 50
暴力:
/*直接暴力n!*/#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define maxn 51using namespace std;int n,p[maxn],q[maxn],s[maxn],ans;int main(){ freopen("swap.in","r",stdin); freopen("swap.out","w",stdout); scanf("%d",&n); for(int i=1;i<n;i++)q[i]=i; for(int i=1;i<=n;i++){ int x;scanf("%d",&x); p[i]=x+1; } do{ for(int i=1;i<=n;i++)s[i]=i; for(int i=1;i<n;i++)swap(s[q[i]],s[q[i]+1]); int falg=0; for(int i=1;i<=n;i++) if(s[i]!=p[i]){ falg=1;break; } if(falg==0)ans++; }while(next_permutation(q+1,q+n)); printf("%d\n",ans);}
9.2noip模拟试题