最近好多構造啊(還不是課件裏那麼難的那種)。。。產生了一種我排名虛高的局面
這套題對我很好。。。一個暴力一個構造一個暴力。
$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 }