神奇的check函数,
任世界千变万化,我check永远是check
1>land
一棵树,求砍cut刀以后,
cut+1棵数中,直径最大值 最小是多少
//因为cut刀怎么砍与直径最大值有关,不好控制, //所以我们通过枚举最大的直径,然后去dfs砍枝 //然后,让我们来见证,这个神奇的二分剪法 #include<cstdio> #include<cstdlib> #include<vector> using namespace std; int n,cut; const int N=4e5+3; int d[N],sz[N]; vector <int> g[N]; int mid,sum; int check(int nw,int f) { int l_mn=mid,r_mx=0,cnt=0; for(int i=0;i<sz[nw];i++) { int u=g[nw][i]; if(u==f) continue; int len=check(u,nw)+1; if(len>mid) sum++; else if(len>(mid>>1)) cnt++,l_mn=min(l_mn,len); else if(len>0) r_mx=max(r_mx,len); } if(cnt) { sum+=cnt; if(l_mn+r_mx<=mid) { sum--; return l_mn; } } return r_mx; } int main() { scanf("%d%d",&n,&cut); int u,v; for(int i=1;i<n;i++) scanf("%d%d",&u,&v),g[u].push_back(v),g[v].push_back(u); for(int i=1;i<=n;i++) sz[i]=g[i].size() ; if(cut+1>=n) printf("0 "); else { int l=0,r=n,ans=0; while(l<=r) { mid=(l+r)>>1;//>cut就是这个解太优了,要把解变差一点,就是把ans的范围往右边调整 sum=0,check(1,0);
if(sum>cut) l=mid+1; else ans=mid,r=mid-1;//重点注意这个地方的写法!!!! . //求的是cut刀,最小ans是多少, //但是有可能此最小ans的时候,最小砍cut-1刀, //所以写的时候,就是<=cut的时候,都更新答案,但是大于不行 } printf("%d ",ans); } return 0; }
2>Best Cow Fences
整理一个知识点:
求最大子序列和
O(n)
单独考虑每个点,前面的最优解>0,就加上,如果<0,就不加,
再选自己,
如果自己加上最优解,都<0,就都别取了
给你一个正整数序列,找出一个区间使得平均值最大,要求该区间的长度大于等于L。
策略:二分答案
//小数二分,令人头大 #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> using namespace std; int n,l; const int N=1e5+3; double d[N]; double dd[N],sum[N],f[N]; bool check(double mid) { memset(sum,0,sizeof(sum)); memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) { dd[i]=d[i]-mid; sum[i]=sum[i-1]+dd[i]; f[i]=max(0.0,dd[i]+max(0.0,f[i-1])); } double ans=-1; for(int i=l;i<=n;i++) ans=max(ans,sum[i]-sum[i-l]+f[i-l]); return (ans>0); } int main() { scanf("%d%d",&n,&l); double mn=2003,mx=0; for(int i=1;i<=n;i++) { scanf("%lf",&d[i]); mn=min(mn,d[i]),mx=max(mx,d[i]); } int cnt=500; double l=mn,r=mx;
while(r-l>=0.00001 && --cnt)//注意,这里是二分小数型写法 { double mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; } r*=10000; printf("%d ",(int)r/10) ; //不四舍五入型写法 return 0; }
3>曲线
就一个二次函数形状的图像,二分找答案最小,
但因为x,y的取值是全实数,所以我二分x,y的时候,要多出来特别多的精度,
大概长这样:r-l>=0.00000001
然后计算复杂,加cnt算了
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int t,n; const int N=1e5+3,inf=-2e9; int a[N],b[N],c[N]; double f(double x) { double ans=inf; for(int i=1;i<=n;i++) ans=max(ans,double(x*x*a[i] +x*b[i] +c[i] ) ); return ans; } int main() { scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]); double l=0,r=1000,as=0; int cnt=400; while(r-l>=0.00000001)//就是精度不够 { double m1=l+(r-l)/3,m2=r-(r-l)/3; double v1=f(m1),v2=f(m2); if(v1<=v2) r=m2,as=v1; else l=m1,as=v2; } printf("%.4f ",as ); } return 0; }
4>生日快乐
一看就是二分,但是是 二分+搜索+小数处理...
题目很好
#include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; int x,y,k; double s; double dfs(double m,double n,int cut) { if(cut==1) return (double)max(m,n)/min(m,n); double ans; double mx_m=s/n,mx_n=s/m; ans=dfs(mx_m,n,1); for(int i=2;i<cut;i++) ans=min(ans,max(dfs(mx_m*i,n,i) , dfs(m-mx_m*i,n,cut-i) )); for(int j=1;j<cut;j++) ans=min(ans,max(dfs(m,mx_n*j,j) , dfs(m,n-mx_n*j,cut-j) )); return ans; } int main() { scanf("%d%d%d",&x,&y,&k); s=x*y*1.0/k; printf("%.6lf ",dfs(x*1.0,y*1.0,k)); return 0; }
4>派
5>皇帝的烦恼