还有3天,今天考试又崩了。状态还没有调整过来。。。
第一题:小L的二叉树
勤奋又善于思考的小L接触了信息学竞赛,开始的学习十分顺利。但是,小L对数据结构的掌握实在十分渣渣。
所以,小L当时卡在了二叉树。 在计算机科学中,二叉树是每个结点最多有两个子结点的有序树。通常子结点被称作“左孩子”和“右孩子”。二叉树被用作二叉搜索树和二叉堆。随后他又和他人讨论起了二叉搜索树。什么是二叉搜索树呢?二叉搜索树首先是一棵二叉树。设key[p]表示结点p上的数值。对于其中的每个结点p,若其存在左孩子lch,则key[p]>key[lch];若其存在右孩子rch,则key[p]<key[rch];注意,本题中的二叉搜索树应满足对于所有结点,其左子树中的key小于当前结点的key,其右子树中的key大于当前结点的key。(因为小L十分喜欢装xx,所以这里他十分装xx的给大家介绍了什么是二叉树和二叉搜索树)。 可是善于思考的小L不甘于只学习这些基础的东西。他思考了这样一个问题:现在给定一棵二叉树,可以任意修改结点的数值。修改一个结点的数值算作一次修改,且这个结点不能再被修改。若要将其变成一棵二叉搜索树,且任意时刻结点的数值必须是整数(可以是负整数或0),所要的最少修改次数。
【数据范围】 20 % :n <= 10 , ai <= 100. 40 % :n <= 100 , ai <= 200 60 % :n <= 2000 . 100 % :n <= 10 ^ 5 , ai < 2 ^ 31.
解:
考试的时候连题都读错了。。是所有子树而不是一个儿子。。
然后由左--父--右可知是一个中序遍历。而中序遍历的数值应该是递增的。所以原数值按中序排列,找最长上升序列,再用n减去就是答案了。但是会出现如2,1,3,4的LIS应该是3,但是实际上因为2,3之间只差了1,而数值只能是整数,所以a[i]-=i保证之间还有空位。
接着,LIS要用nlogn的算法。注意,这里因为已经保证有空位了,所以是最长不下降序列。然后就不再是lower_bound而是upper_bound,因为要修改的应该是第一个比它大的,而不是大于等于它的。如下图:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define maxn 100005 6 #define ll long long 7 #ifdef WIN32 8 #define AUTO "%I64d" 9 #else 10 #define AUTO "%lld" 11 #endif 12 using namespace std; 13 int n,idx,dp[maxn]; 14 ll ai[maxn],v[maxn]; 15 struct pp{ 16 int lch,rch; 17 }; 18 pp tree[maxn]; 19 void dfs(int x) 20 { 21 if (tree[x].lch) dfs(tree[x].lch); 22 v[++idx]=x; 23 if (tree[x].rch) dfs(tree[x].rch); 24 } 25 int main() 26 { 27 freopen("tree.in","r",stdin); 28 freopen("tree.out","w",stdout); 29 cin>>n; 30 for (int i=1;i<=n;i++) 31 scanf(AUTO,&ai[i]); 32 for (int i=2;i<=n;i++) 33 { 34 int x,y; 35 scanf("%d%d",&x,&y); 36 if (y==0) tree[x].lch=i; 37 else tree[x].rch=i; 38 } 39 dfs(1); 40 for (int i=1;i<=n;i++) 41 v[i]=ai[v[i]]-i;///******* 42 dp[1]=1;/* 43 for (int i=2;i<=n;i++) 44 { 45 int l=0; 46 for (int j=1;j<i;j++) 47 { 48 if(v[i]>v[j]&&dp[j]>l) l=dp[j]; 49 } 50 dp[i]=l+1; 51 } 52 int ans=0; 53 for (int i=1;i<=n;i++) 54 if (dp[i]>ans) ans=dp[i];*/ 55 int ans=0; 56 for (int i=1;i<=n;i++) 57 { 58 if (v[i]>=dp[ans]) dp[++ans]=v[i];//>= 59 else { 60 int pos=upper_bound(dp+1,dp+1+ans,v[i])-dp; 61 dp[pos]=v[i]; 62 } 63 } 64 printf("%d",n-ans); 65 return 0; 66 }
第二题:小L的牛栏
小L通过泥萌的帮助,成功解决了二叉树的修改问题,并因此写了一篇论文, 成功报送了叉院(羡慕不?)。勤奋又勤思的他在研究生时期成功转系,考入了北京大学光华管理学院!毕业后,凭着自己积累下的浓厚经济学与计算机学的基础,成功建设了一个现代化奶牛场! 奶牛们十分聪明,于是在牛场建围栏时打算和小L斗智斗勇!小L有N种可以建造围栏的木料,长度分别是l1,l2„lN,每种长度的木料无限。
修建时,他将把所有选中的木料拼接在一起,因此围栏的长度就是他使用的木料长度之和。但是聪明的小L很快发现很多长度都是不能由这些木料长度相加得到的,于是决定在必要的时候把这些木料砍掉一部分以后再使用。
不过由于小L比较节约,他给自己规定:任何一根木料最多只能削短M米。当然,每根木料削去的木料长度不需要都一样。不过由于测量工具太原始,小L只能准确的削去整数米的木料,因此,如果他有两种长度分别是7和11的木料,每根最多只能砍掉1米,那么实际上就有4种可以使用的木料长度,分别是6, 7,10, 11。
因为小L相信自己的奶牛举世无双,于是让他们自己设计围栏。奶牛们不愿意自己和同伴在游戏时受到围栏的限制,于是想***难一下小L,希望小L的木料无论经过怎样的加工,长度之和都不可能得到他们设计的围栏总长度。不过小L知道,如果围栏的长度太小,小L很快就能发现它是不能修建好的。因此她希望得到你的帮助,找出无法修建的最大围栏长度。
40 % :1< N<10, 0< M< 300
100 % :1< N< 100, 0< M< 3000
解: 新方法:同余最短路。
1.首先预处理出所有能搞出来的原始木棍长度,然后找到一个最小的,记为P。如果P=1那就不用做下去了,直接输出-1.
2.我们把所有的整数按mod P 的值分为P 类(mod P=0,1,2,3,4...P-1),记为集合Q0,Q1,Q2...QP-1。如果集合Qi 中有一个长度len 可以被组合出来,那么该集合中所有比len 大的数也一定可以组合出来.因为是mod P 的,所以len 可以不断加P 来组合出比它大的且和它在同一个集合里的数。根据这个性质就可以找到图论模型了。
3.我们抽象出P-1 个点,分别表示集合Qi 中最小的能被组合出来的数D[i]。那么把根据原始木棍的长度,可以在这些点之间连边,表示可以从Qi 中的一个数加X 得到Qj中的一个数。跑一遍SPFA找每个集合中的最小能被组合出来的数;
4、集合Qi 中最大的不能被组合出来的数就是D[i]-P,在这之中找一个最大的不能被组合的。
5、注意,如果有一个D[i]没被赋值,即这个集合中的所有数都不能被组合,而且是无限大的。所以还是输出-1;
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #define maxn 3005 7 using namespace std; 8 const int INF=0x3f3f3f3f; 9 int n,m,len[maxn],idx,mi=12345678,ans=0; 10 int q[maxn]; 11 bool can[maxn],b[maxn]; 12 queue<int> que; 13 void spfa() 14 { 15 memset(q,INF,sizeof(q));//赋为最大初值** 16 q[0]=0;que.push(0);b[0]=true; 17 while(!que.empty()) 18 { 19 int u=que.front(); 20 que.pop(); 21 b[u]=false; 22 for (int i=2;i<=idx;i++) 23 { 24 int v=(u+len[i])%mi; 25 if (q[v]>q[u]+len[i]/*&&!b[v]*/) 26 { 27 q[v]=q[u]+len[i]; 28 if (b[v]) continue;//不能写在里面,因为还要更新q[v] 29 b[v]=true; 30 que.push(v); 31 } 32 } 33 } 34 } 35 int main() 36 { 37 freopen("bullpen.in","r",stdin); 38 freopen("bullpen.out","w",stdout); 39 scanf("%d%d",&n,&m); 40 for (int i=1;i<=n;i++) 41 { 42 int x=0; 43 scanf("%d",&x); 44 for (int j=0;j<=m;j++) 45 { 46 if (x-j<=0) break; 47 can[x-j]=true; 48 if (x-j<mi) mi=x-j; 49 } 50 } 51 for (int i=1;i<=maxn;i++) 52 if (can[i]) len[++idx]=i; 53 if (mi==1) { 54 printf("-1"); 55 return 0; 56 } 57 spfa(); 58 for (int i=0;i<=mi-1;i++) 59 { 60 if (ans<q[i]) ans=q[i]; 61 if (q[i]>=INF) { 62 printf("-1"); 63 return 0; 64 } 65 } 66 67 printf("%d",ans-mi); 68 return 0; 69 }
第三题:小L的珍珠挂饰
小L通过泥萌的帮助,成功解决了牛栏的修建问题。奶牛们觉得主人非常厉害,于是再也不敢偷懒,母牛们奋力挤奶,生娃。子子孙孙无穷匮也!小L于是成为了一代富豪! 但是一直困扰小L的就是单身问题!小L经过长久的寻觅,小L终于找到了一个心仪的漂亮妹子。于是,小L打算在520那天给妹子一个惊喜!(虽然小L很节约,但是对妹子还是很阔绰的!) 小L决定用K种珍珠为妹子做一串举世无双的珍珠垂饰。珍珠垂饰是由珍珠连接而成的,其长度可以认为就是珍珠垂饰上珍珠的个数。小L现在腰缠万贯,每种珍珠他都拥有N颗。根据将珍珠垂饰打开后珍珠不同的排列顺序可以区别不同种类的项链。现在,小L好奇自己可以组成多少种长度为1至N的不同的珍珠垂饰?当然,为显富有,每串珍珠垂饰都要必须由K种珍珠连成。 答案取模1234567891。
40 % :1<= N<=100000, 0<=K<=30
70 % :1<= N<= 1000000000, 0<=K<=30时限 :1000ms
80%~100% :T <= 10, 1<= N<= 1000000000, 0<=K<=30 时限:50ms
解:
40%:递推。。i 颗珍珠,取 j 种:f[i][j]=f[i-1][j-1]*(k-j+1)【第 i 个取一个新的种类,有k-j+1种取法】+f[i-1][j]*j【第i个取一个与取过的重复的】 初值:f[1][1]=k f[i][0]=1
100%:矩阵快速幂。挖坑