Noip模拟66 2021.10.2
T1 接力比赛
思路就是直接做背包$dp$,然后看看容量相同的相加的最大值。
考虑如何在$dp$过程中进行优化
注意到转移方程的第二维枚举容量没有必要从容量总和开始枚举
那么我们便转移边统计前缀和,从前缀和到当前容量做转移就行
复杂度$???$,可以过
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 inline int read(){ 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 13 }using namespace AE86; 14 const int NN=1e3+1; 15 int n,m,ans,f[1000005],sm[NN],g[1000005],tot; 16 struct node{int w,v;}p[NN]; 17 namespace WSN{ 18 inline short main(){ 19 freopen("game.in","r",stdin); 20 freopen("game.out","w",stdout); 21 n=read(); m=read(); 22 memset(f,-0x3f,sizeof(f)); 23 memset(g,-0x3f,sizeof(g)); 24 f[0]=g[0]=0; 25 for(int i=1;i<=n;i++) 26 p[i].w=read(),p[i].v=read(),sm[i]=sm[i-1]+p[i].w; 27 tot=max(tot,sm[n]); 28 for(int i=1;i<=n;i++) 29 for(int j=sm[i];j>=p[i].w;j--) 30 f[j]=max(f[j],f[j-p[i].w]+p[i].v); 31 memset(sm,0,sizeof(sm)); 32 for(int i=1;i<=m;i++) 33 p[i].w=read(),p[i].v=read(),sm[i]=sm[i-1]+p[i].w; 34 tot=max(tot,sm[m]); 35 for(int i=1;i<=m;i++) 36 for(int j=sm[i];j>=p[i].w;j--) 37 g[j]=max(g[j],g[j-p[i].w]+p[i].v); 38 for(int i=0;i<=tot;i++){ 39 ans=max(ans,f[i]+g[i]); 40 } write(ans); 41 return 0; 42 } 43 } 44 signed main(){return WSN::main();}
T2 树上竞技
考场上以为是道数据结构加$dp$套路题,结果是道纯计数
考虑每条边做的贡献,设一条边左边有$s$个点,右边则有$n-s$
那么中心点,也就是所有点都要走到的那个点一定出在 点数多的一侧
也就是这条边会做$min(i,m-i)$次贡献,$i$是左边要选的点的个数。
那么答案可以写成$ans=\sum _{u=2}^{n} \sum_{i=1}^{m-1} C_{siz[u]}^{i} *C_{n-siz[u]}^{m-i}*min(i,m-i)$
考虑把$min$干掉,柿子内部化简为$\sum_{i=1}^{\frac{m-1}{2}} C_{s}^{i} *C_{n-s}^{m-i}*i+C_{n-s}^{i}*C_{s}^{m-i}*i$
当然如果$m$是偶数还要加上特殊的没进行考虑的$C_{s}^{\frac{m}{2}}*C_{n-s}^{\frac{m}{2}}* \frac{m}{2}$
题解柿子错了
然后这样或者不去掉$min$可获得$40$。
偶数的那部分可以直接暴力,我们继续化简$ \sum_{i=1}^{k} C_{s}^{i} *C_{n-s}^{m-i}*i$一部分
设$G[s]=\sum_{i=1}^{k} C_{s}^{i} *C_{n-s}^{m-i}*i=s*\sum_{i=1}^{k} C_{s-1}^{i-1} *C_{n-s}^{m-i}$
那么答案为$ans=\sum_{u=2}^{n}G[siz[u]]+G[n-siz[u]]$
考虑$\frac{G[s]}{s}$的组合含义,从$n-1$个物品中选择$m-1$个物品,至少有$k$个物品在前$s$个物品内的方案
考虑$G$之间的递推性,可以感性理解,画一个数轴自己挪一挪分界点就出来了,$G[s]-G[s+1]=C_{s-1}^{k-1}*C_{n-s-1}^{m-k-1}$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 inline int read(){ 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 13 }using namespace AE86; 14 const int NN=1e6+1,mod=1e9+7; 15 int n,m,h[NN],v[NN],ans; 16 int siz[NN],fa[NN],G[NN]; 17 struct SNOW{int to,next;}e[NN<<1];int head[NN],rp; 18 inline void add(int x,int y){ 19 e[++rp]=(SNOW){y,head[x]};head[x]=rp; 20 e[++rp]=(SNOW){x,head[y]};head[y]=rp; 21 } 22 inline void dfs1(int f,int x){ 23 siz[x]=1; 24 for(int i=head[x];i;i=e[i].next){ 25 int y=e[i].to ; if(y==f) continue; 26 dfs1(x,y); siz[x]+=siz[y]; 27 } 28 } 29 inline int qmo(int a,int b,int ans=1){ 30 int c=mod; a%=c;for(;b;b>>=1,a=a*a%c) if(b&1) ans=ans*a%c; 31 return ans; 32 } 33 inline void pre(){ 34 h[0]=h[1]=1; v[0]=v[1]=1; 35 for(int i=2;i<NN;++i) h[i]=h[i-1]*i%mod; 36 v[NN-1]=qmo(h[NN-1],mod-2); 37 for(int i=NN-2;i>=2;--i) v[i]=v[i+1]*(i+1)%mod; 38 } 39 inline int C(int n,int m){ 40 if(n<m||n<0||m<0) return 0; 41 return h[n]*v[n-m]%mod*v[m]%mod; 42 } 43 namespace WSN{ 44 inline short main(){ 45 freopen("meeting.in","r",stdin); 46 freopen("meeting.out","w",stdout); 47 n=read();m=read(); pre(); 48 for(int i=2;i<=n;++i) 49 fa[i]=read(),add(fa[i],i); 50 dfs1(0,1); 51 if(m%2==0) for(int i=2;i<=n;i++) 52 ans=(ans+C(siz[i],m/2)*C(n-siz[i],m/2)%mod*(m/2)%mod)%mod; 53 int k=(m-1)>>1; if(k) G[1]=C(n-1,m-1); 54 for(int i=1;i<n;i++) G[i+1]=(G[i]-C(i-1,k-1)*C(n-i-1,m-k-1)%mod+mod)%mod; 55 for(int i=1;i<=n;i++) G[i]=G[i]*i%mod; 56 for(int i=2;i<=n;i++) 57 ans=(ans+G[siz[i]]+G[n-siz[i]])%mod; 58 write(ans); 59 60 return 0; 61 } 62 } 63 signed main(){return WSN::main();}
T3 虚构推理
水过的,直接枚举一天的所有时间然后找最大值的最小值即可,跑的比正解快仅有$400ms$
不懂为啥能$zhe$快。
正解是二分,二分出一个角度,$check$的时候找有无区间覆盖,
区间是指每一个输入指针加减验证的角度得出的一段可能值域
那么对于区间的交可以用柯朵莉树
1 #include<bits/stdc++.h> 2 #define int long long 3 #define wsn double 4 using namespace std; 5 namespace AE86{ 6 inline int read(){ 7 int x=0,f=1;char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 9 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 10 }inline void write(int x,char opt='\n'){ 11 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 12 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 13 for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 14 }using namespace AE86; 15 const int NN=50005; 16 int n; 17 wsn a[NN],b[NN],ans=1e9; 18 inline wsn abs(wsn a,wsn b){ 19 a=fabs(a-b); return a>180 ? 360-a:a; 20 } 21 inline wsn calc(wsn x,wsn s[]){ 22 int i=lower_bound(s+1,s+n+1,x>180?x-180:x+180)-s,j=i-1; 23 if(i>n) i=1; if(j==0) j=n; 24 return max(abs(s[i],x),abs(s[j],x)); 25 } 26 namespace WSN{ 27 inline short main(){ 28 freopen("unreal.in","r",stdin); 29 freopen("unreal.out","w",stdout); 30 n=read(); 31 for(int i=1,h,m,s;i<=n;i++){ 32 h=read(),m=read(),s=read(); 33 b[i]=s*0.1+m*6.0; 34 a[i]=(h%12)*30.0+b[i]/12.0; 35 } 36 sort(a+1,a+n+1); sort(b+1,b+n+1); 37 for(int h=0;h<360;h+=30) 38 for(wsn m=0;m<360;m+=0.01) 39 ans=min(ans,max(calc(h+m/12,a),calc(m,b))); 40 printf("%.10lf\n",ans); 41 return 0; 42 } 43 } 44 signed main(){return WSN::main();}
T4 记忆碎片
咕咕咕