[考试反思]0512省选模拟94:畏惧
最近好多构造啊(还不是课件里那么难的那种)。。。产生了一种我排名虚高的局面
这套题对我很好。。。一个暴力一个构造一个暴力。
$T1$的正解是神奇的计算几何。幸亏没想到,不然真不知道要浪费多少时间。。。
赛后调一个裸的凸包模板调了好几个小时(并不怎么理解)。。。
心烦。。。$LCT$和计算几何真是俩很难迈过去的坎
T1:a(终)
大意:$n$点有权值$A_i,B_i$。构造一条回路(不需要经过所有点),满足$B$最小的点到$B$最大的点路径上$B$非严格递增,最大到最小非严格递减。
走一步$i \to j$的贡献是$\frac{(A_i-A_j)B_iB_j}{A_iA_j}$。最大化代价。$n \le 10^5,0 <|A| \le 100$
贡献的式子可以简单化为$(A_i \frac{A_j}{B_j})-(A_j \frac{A_i}{B_i})$
$(A,\frac{A}{B})$。向量叉积形式。发现求的就是面积。维护凸包即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 100005 4 int a[S],b[S],n,t; long double ans; 5 struct P{ 6 long double x,y; 7 friend bool operator<(P a,P b){return fabs(a.x-b.x)>1e-7?a.x<b.x:a.y<b.y;} 8 long double operator^(P b){return x*b.y-y*b.x;} 9 P operator-(P b){return (P){x-b.x,y-b.y};} 10 }p[S],q[S]; 11 int main(){ 12 scanf("%d",&n); 13 for(int i=1;i<=n;++i)scanf("%d",&a[i]),b[i]=b[i-1]+a[i],p[i]=(P){b[i],1.*b[i]/a[i]}; 14 sort(p+1,p+1+n); 15 for(int i=1;i<=n;++i){ 16 while(t>1&&(q[t]-q[t-1]^p[i]-q[t])<1e-7)t--; 17 q[++t]=p[i]; 18 } 19 int lim=t; 20 for(int i=n-1;i;--i){ 21 while(t>lim&&(q[t]-q[t-1]^p[i]-q[t])<1e-7)t--; 22 q[++t]=p[i]; 23 } 24 for(int i=1;i<=t;++i)ans+=q[i]^q[i%t+1]; 25 printf("%.5Lf\n",ans/2); 26 }
T2:b(壕)
大意:数列,$a_i \in [0,6]$。每次操作可以选择一个区间整体加一个数后对$7$取模。最小化全变成$0$的步数。$n \le 500$
区间加差分转化为一点加一点减。(为了方便原序列末尾需要加一个$0$)。这样的话一次操作后序列元素的和不会变。
对于一个大小为$k$的集合。我们把第一个数加成$0$并等量减第二个数,后续同理,则把$k-1$个数弄成$0$时最后一个数一定也是$0$
所以说对于和为$0$的大小为$x$的集合需要$x-1$次操作。所以总操作次数就是$n+1-cnt$。$cnt$是划分的集合数。故要最大化权值和为$0$的集合。
元素$0$肯定单独成一个集合了。下面证明如果$x$和$7-x$同时存在那么它们配对一定最优:
如果存在一种方案使得$x\in A,7-x \in B$。要求$A,B$均不可再分裂成两个权值和为$0$的子集。
对于这种情况我们把$x,7-x$单独拿出来做一个集合。$A,B$的剩余部分构成另一个集合,这样的话集合数没有变。
但是新集合可能能分裂成两个小的权值和为$0$的集合使总集合数变多。所以说$x,7-x$配对答案可能更优而不会变差。
所以$x,7-x$两两配对之后较少的一种就用完了。$(1,6),(2,5),(3,4)$每对只剩下一个。
也就是只剩下最多$3$种元素参加配对。这样直接做一个$O(n^3)$的$dp$就行了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int dp[502][502][502],a[501],n,b[8],ans,A,B,C,u[7][7]; 4 int mo(int x){x=x%7+7;return x>=7?x-7:x;} 5 int main(){ 6 scanf("%d",&n); 7 for(int i=1;i<=n;++i)scanf("%d",&a[i]),b[mo(a[i]-a[i-1])]++; 8 b[mo(-a[n])]++; n++; 9 b[7]=b[0]; for(int i=0,x;i<4;++i)x=min(b[i],b[7-i]),ans+=x,b[i]-=x,b[7-i]-=x; 10 for(int i=1;i<8;++i)if(b[i])(A?(B?C:B):A)=i; 11 for(int i=0;i<7;++i)for(int j=0;j<7;++j){ 12 int x=mo(i*A+j*B); 13 while(u[i][j]<=b[C]&&x)++u[i][j],x=mo(x+C); 14 }u[0][0]=7; 15 for(int i=b[A];i>=0;i-=7)for(int j=b[B];j>=0;j-=7)for(int k=b[C];k>=0;k-=7) 16 dp[i][j][k]=(b[A]-i+b[B]-j+b[C]-k)/7; 17 for(int i=b[A];~i;--i)for(int j=b[B];~j;--j)for(int k=b[C];~k;--k) 18 for(int x=0;x<7&&x<=i;++x)for(int y=0;y<7&&y<=j;++y)if(k>=u[x][y]) 19 dp[i-x][j-y][k-u[x][y]]=max(dp[i-x][j-y][k-u[x][y]],dp[i][j][k]+1); 20 printf("%d\n",n-(ans+dp[0][0][0])); 21 }
T3:c(息)
大意:给定$F_{0,i}$,$F_{x,i}=F_{x-1,i}+F_{x,i-1}$。支持修改$F_{0,i}$或查询$F_{x,i}$。$i \le 10^5,x \le 20,m \le 10^5$
一种暴力做法是每次修改的时候重置整个$F$表。修改$O(20n)$查询$O(1)$
一种暴力做法是用组合数统计每个修改对某一个询问的贡献。修改$O(1)$查询$O(m)$
均摊一下。每$\sqrt{20n}$定期重置$F$表并清空修改栈。$O(n\sqrt{n})$
1 #include<cstdio> 2 const int mod=1000000007,S=100055; const long long mxv=8000000000000000000; 3 int F[21][S],n,m,c[S],C[S][21]; 4 struct my_set{ 5 bool al[S];int v[S],cnt; 6 void ins(int x){if(!al[x])al[x]=1,v[++cnt]=x;} 7 void clear(){while(cnt)al[v[cnt--]]=0;} 8 }M; 9 int mo(int a){return a>=mod?a-mod:a;} 10 void Mo(long long&x){if(x>=mxv||x<=-mxv)x%=mod;} 11 void rebuild(){ 12 for(int i=1;i<=n;++i)F[0][i]=c[i]; 13 for(int t=1;t<=20;++t)for(int i=1;i<=n;++i)F[t][i]=mo(F[t-1][i]+F[t][i-1]); 14 M.clear(); 15 } 16 int main(){ 17 scanf("%d%d",&n,&m); 18 for(int i=C[0][0]=1;i<=n+20;++i)for(int j=C[i][0]=1;j<=20;++j)C[i][j]=mo(C[i-1][j]+C[i-1][j-1]); 19 for(int i=1;i<=n;++i)scanf("%d",&c[i]); 20 rebuild(); 21 for(int i=1,o,x,y;i<=m;++i){ 22 scanf("%d%d%d",&o,&x,&y); 23 if(o==1)c[x]=y,M.ins(x); 24 else{ 25 if(x==0){printf("%d\n",c[y]);continue;} 26 if(M.cnt>1414)rebuild(); 27 long long ans=F[x][y]; 28 for(int i=1;i<=M.cnt;++i)if(M.v[i]<=y)ans+=(0ll+c[M.v[i]]-F[0][M.v[i]])*C[y-M.v[i]+x-1][x-1],Mo(ans); 29 printf("%d\n",mo(ans%mod+mod)); 30 } 31 } 32 }