Nescafe #29 NOIP模拟赛
不知道这种题发出来算不算侵权...毕竟有的题在$bz$上是权限题,但是在$vijos$似乎又有原题...如果这算是侵权的话请联系我,我会尽快删除,谢谢~
今天开始就处于一种半停课状态了.学校学习衡水,把休息时间压缩后每天又多了两节自习来写作业,但是我正好就可以来机房啦2333
因为考试时间被文化课分割成了一些散块,这里就不区分考试时的代码和赛后补题了.
T1:穿越七色虹
题意概述:给出$x$轴上的$7$个圆(圆心和半径)(只保留$x$轴以上的部分),可以将所有圆的半径同时增加一个非负数,要求$(0,0)$到$(X_0,h)$这一块矩形面积被完全覆盖。
其实这题读题稍微有点困难,不过概括之后就好理解多了。
其实这题思路挺显然的,半径越大肯定覆盖的面积越大,所以直接二分这个扩大的值,扩大之后算出每个圆可以覆盖的高度为$h$的矩形的左右端点,再做一个线段覆盖即可.这里注意一个问题:$check$时会首先把每个半径加上一个数,最后再减掉,如果是这样的做法就一定要注意不能在函数进行到一半时就$return$掉,否则这个增量就会一直留在那里啦.

1 # include <cstdio> 2 # include <iostream> 3 # include <cmath> 4 # include <algorithm> 5 6 using namespace std; 7 8 const double eps=0.001; 9 double h,x0; 10 double x[8],rr[8],l,r,mid,ans; 11 struct lin 12 { 13 double l,r; 14 }a[8]; 15 16 bool check (double ad) 17 { 18 double maxr=0,minl=100000; 19 for (int i=1;i<=7;++i) 20 { 21 a[i].l=x[i]-sqrt((rr[i]+ad)*(rr[i]+ad)-h*h); 22 a[i].r=x[i]+sqrt((rr[i]+ad)*(rr[i]+ad)-h*h); 23 minl=min(minl,a[i].l); 24 } 25 if(minl>eps) return false; 26 for (int i=1;i<=7;++i) 27 for (int j=1;j<=7;++j) 28 if(a[j].l-maxr<=eps) maxr=max(maxr,a[j].r); 29 if(maxr-x0>=-eps) return true; 30 return false; 31 } 32 33 int main() 34 { 35 scanf("%lf%lf",&h,&x0); 36 for (int i=1;i<=7;++i) 37 scanf("%lf%lf",&x[i],&rr[i]); 38 l=0,r=sqrt(x0*x0+h*h); 39 ans=r+4; 40 41 while (r-l>=-eps) 42 { 43 mid=(l+r)/2.0; 44 if(check(mid)) 45 ans=min(ans,mid),r=mid-eps; 46 else l=mid+eps; 47 } 48 printf("%.2lf",ans); 49 return 0; 50 }
T2:四叶草魔杖
题意概述:给出$n$个点$m$条边,每个点有一个能量值,如果是负的意味着要有这么多能量通过边运输过来,正的则为要运走这么多,每条边只要用到就会产生相应的代价,求使所有点的能量值都变为$0$所需要的最小代价.$n<=16$
看到$n$这么小的范围当然会想到搜索状压$dp$.首先所有点都被连到一起必然是合法的,但是想要合法并不需要所有点都连在一起.首先一遍状压$dp$求出每个联通块联通的最小代价,统计一下哪些联通块的权值和是$0$,把这些状态单独拿出来再进行一次状压$dp$即可,虽然复杂度不是非常科学,但是跑的还是挺快的.

1 # include <cstdio> 2 # include <iostream> 3 # include <cstring> 4 # define R register int 5 6 using namespace std; 7 8 const int maxn=16; 9 const int maxz=(1<<16); 10 int n,m,h,firs[maxn]; 11 int dp[maxz+5],a[maxn],x,y,co,j; 12 struct edge 13 { 14 int too,nex,co; 15 }g[maxn*maxn]; 16 int k[maxz+5],Top=0,c[maxz+5]; 17 18 void add (int x,int y,int co) 19 { 20 g[++h].co=co; 21 g[h].nex=firs[x]; 22 firs[x]=h; 23 g[h].too=y; 24 } 25 26 int main() 27 { 28 memset(dp,-1,sizeof(dp)); 29 dp[0]=0; 30 scanf("%d%d",&n,&m); 31 for (R i=0;i<n;++i) 32 scanf("%d",&a[i]); 33 for (R i=0;i<n;++i) 34 dp[1<<i]=0; 35 for (R i=1;i<=m;++i) 36 { 37 scanf("%d%d%d",&x,&y,&co); 38 add(x,y,co); 39 add(y,x,co); 40 } 41 for (R z=0;z<=(1<<n)-1;++z) 42 { 43 if(dp[z]==-1) continue; 44 for (R i=0;i<n;++i) 45 { 46 if((z&(1<<i))==0) continue; 47 for (R b=firs[i];b;b=g[b].nex) 48 { 49 j=g[b].too; 50 if(z&(1<<j)) continue; 51 if(dp[z|(1<<j)]==-1) 52 dp[z|(1<<j)]=dp[z]+g[b].co; 53 else 54 dp[z|(1<<j)]=min(dp[z|(1<<j)],dp[z]+g[b].co); 55 } 56 } 57 } 58 for (R i=1;i<=(1<<n)-1;++i) 59 { 60 int s=0; 61 if(dp[i]==-1) continue; 62 for (R j=0;j<n;++j) 63 if((1<<j)&i) s+=a[j]; 64 if(s==0) k[++Top]=i,c[Top]=dp[i]; 65 } 66 memset(dp,-1,sizeof(dp)); 67 dp[0]=0; 68 for (R i=0;i<=(1<<n)-1;++i) 69 { 70 if(dp[i]==-1) continue; 71 for (R j=1;j<=Top;++j) 72 { 73 if(dp[ i|k[j] ]==-1) dp[ i|k[j] ]=dp[i]+c[j]; 74 else dp[ i|k[j] ]=min(dp[ i|k[j] ],dp[i]+c[j]); 75 } 76 } 77 if(dp[(1<<n)-1]==-1) printf("Impossible"); 78 else printf("%d",dp[(1<<n)-1]); 79 return 0; 80 }
T3:圣主的考验
题意概述:定义一棵合法的二叉树为:每个点的左右儿子的高度差不大于$1$,求包含$n$个节点的树有多少种可能形态.$n<=3000$.注意:如果答案大于九位就输出后九位(包括前导零),如果小于九位就原样输出.
朴素的$dp$不是很难想,但是是$n^3$的,现在在这个基础上考虑优化:(我又把$vscode$找出来啦,虽然编译的部分失效了,但是即使只是做文本编辑器也足够好看了)
这个复杂度很显然是跑不满的,尤其是第二维,因为这道题的限制卡的比较严格,所以左右子树的节点数量大致上不会相差非常多,也就是说树高趋向于$log$级别.完全二叉树的深度是所有可能中最浅的,最深的深度也不会大很多,最极端的情况是每个左儿子都比右儿子的深度大,不过直接认为是$logi$加上一个小一点的数字也是可以的.如果发现一个多维$dp$的复杂度过高而明显跑不满时,可以将自己觉得最有可能跑不满的一维直接改小,比如这里的$j$直接改成$1-10$可以得到$90$.关于输出:打表发现当$n$大于等于$36$的时候答案会大于九位...

1 # include <cstdio> 2 # include <iostream> 3 # define R register int 4 # define mod 1000000000 5 6 using namespace std; 7 8 const int maxn=3005; 9 int n,lg[maxn]; 10 long long dp[maxn][31],s[maxn]; 11 12 void init() 13 { 14 dp[0][0]=1; 15 dp[1][1]=1; 16 s[1]=1; 17 for (R i=2;i<=3000;++i) 18 for (R j=lg[i]+1;j<=lg[i]+5;++j) 19 { 20 for (R l=0;l<=i;++l) 21 { 22 if(i-l-1<0) break; 23 dp[i][j]+=dp[l][j-1]*dp[i-l-1][j-2]; 24 dp[i][j]%=mod; 25 dp[i][j]+=dp[l][j-2]*dp[i-l-1][j-1]; 26 dp[i][j]%=mod; 27 dp[i][j]+=dp[l][j-1]*dp[i-l-1][j-1]; 28 dp[i][j]%=mod; 29 } 30 s[i]+=dp[i][j]; 31 s[i]%=mod; 32 } 33 } 34 35 int main() 36 { 37 scanf("%d",&n); 38 for (R i=2;i<=3000;++i) 39 lg[i]=lg[i/2]+1; 40 init(); 41 while (n) 42 { 43 if(n>=36) printf("%09lld ",s[n]); 44 else printf("%lld ",s[n]); 45 scanf("%d",&n); 46 } 47 return 0; 48 }
---shzr