2021.9.17考试总结[NOIP模拟55]
有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟
吓得我直接文件打错
T1 Skip
设状态$f_i$为最后一次选$i$在$i$时的最优解。有$f_i=max_{j<i}[f_j+a_i-\frac{(j-i)\times (j-i-1)}{2}]$
设$j<k$,对$i$来说,$k$优于$j$,当且仅当$2\times i>\frac{2\times(f_j-f_k)+k^2+k-j^2-j}{k-j}$
斜率优化,$CDQ$分治,先按$a$排序,分治中按$id$排序满足限制,然后维护右下凸包更新答案。
$code:$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 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(); } 10 return x*f; 11 } 12 inline void write(int x,char sp){ 13 char ch[20]; int len=0; 14 if(x<0){ putchar('-'); x=~x+1; } 15 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int &x,int &y){ x^=y^=x^=y; } 21 inline void chmax(int &x,int y){ x=x<y?y:x; } 22 inline void chmin(int &x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 const int NN=1e5+5; 26 int n,ans,h,t,tmp,q[NN]; 27 inline int calc(int x){ return x*(x+1)/2; } 28 struct node{ 29 int a,pre,id,f; 30 }f[NN]; 31 bool cmp1(node x,node y){ return x.id<y.id; } 32 bool cmp2(node x,node y){ return x.a==y.a?x.id<y.id:x.a<y.a; } 33 bool cmp3(int x,int y,int z){ return (2*(f[z].f-f[x].f)+f[x].pre-f[z].pre)*(f[y].id-f[z].id)>=(2*(f[z].f-f[y].f)+f[y].pre-f[z].pre)*(f[x].id-f[z].id); } 34 bool cmp4(int x,int y,int a){ return 2*(f[y].f-f[x].f)+f[x].pre-f[y].pre>=2*a*(f[x].id-f[y].id); } 35 36 void solve(int l,int r){ 37 if(l>=r) return; 38 int mid=l+r>>1; 39 solve(l,mid); h=1; t=0; tmp=l; 40 sort(f+l,f+mid+1,cmp1); sort(f+mid+1,f+r+1,cmp1); 41 for(int i=mid+1;i<=r;i++){ 42 while(f[tmp].id<f[i].id&&tmp<=mid){ 43 while(h<t&&!cmp3(tmp,q[t],q[t-1])) --t; 44 q[++t]=tmp++; 45 } 46 while(h<t&&!cmp4(q[h+1],q[h],f[i].id)) ++h; 47 if(h>t) continue; 48 chmax(f[i].f,f[q[h]].f+f[i].a-calc(f[i].id-f[q[h]].id-1)); 49 } 50 sort(f+mid+1,f+r+1,cmp2); 51 solve(mid+1,r); 52 } 53 54 signed main(){ 55 freopen("skip.in","r",stdin); 56 freopen("skip.out","w",stdout); 57 n=read(); ans=-calc(n); 58 for(int i=1;i<=n;i++) f[i].a=read(), f[i].f=f[i].a-calc(i-1), f[i].id=i, f[i].pre=i*i+i; 59 sort(f+1,f+n+1,cmp2); solve(1,n); 60 for(int i=1;i<=n;i++) chmax(ans,f[i].f-calc(n-f[i].id)); 61 write(ans,'\n'); 62 return 0; 63 }
T2 String
爆搜,记忆化方案数。
$code:$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 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(); } 10 return x*f; 11 } 12 inline void write(int x,char sp){ 13 char ch[20]; int len=0; 14 if(x<0){ putchar('-'); x=~x+1; } 15 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int& x,int& y){ x^=y^=x^=y; } 21 inline void chmax(int& x,int y){ x=x<y?y:x; } 22 inline void chmin(int& x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 int k,n,base,finl,tim[26],chr[10]; 26 char s[1000]; 27 map<int,int>f[10]; 28 29 inline int bit(int x){ return x?1<<4*x-4:0; } 30 int dfs(int l,int st,int rk,int lst){ 31 if(f[lst].find(st)!=f[lst].end()&&f[lst][st]<=rk) return f[lst][st]; 32 if(st==finl){ 33 if(!rk){ puts(s); exit(0); } 34 return f[lst][st]=1; 35 } 36 int tmp=0,sum=0; 37 for(int i=base;i<26;i++) if(i+'a'!=s[l-1]){ 38 ++tim[i]; ++chr[tim[i]]; s[l]=i+'a'; 39 if(chr[tim[i]]<=k-tim[i]+1) 40 tmp=dfs(l+1,st+bit(tim[i])-bit(tim[i]-1),rk,tim[i]), rk-=tmp, sum+=tmp; 41 --chr[tim[i]]; --tim[i]; 42 } 43 return f[lst][st]=sum; 44 } 45 46 signed main(){ 47 freopen("string.in","r",stdin); 48 freopen("string.out","w",stdout); 49 k=read(); n=read(); 50 while(k>8){ 51 for(int i=1;i<k;i++) putchar(base+'a'), putchar(base+'b'); 52 putchar(base+'a'); k-=2; base+=2; 53 } 54 for(int i=1;i<=k;i++) finl|=bit(i); 55 dfs(0,0,n-1,9); 56 puts("-1"); 57 return 0; 58 }
T3 Permutation
发现题解是错的,于是花了一下午把它改对了。
首先不难发现可以把问题转化为$n=n+m-k$,$k=m$的情况,因为$m$后的串对答案无影响,且对答案有贡献时后面的串是固定的。设$f_{n,k}$为$n=n,k=k$时的答案。
讨论当前考虑区间的第一个数是否不变。当变化时,设前一行第一个数为$x$,后一行为$x+1$,那么前一行$[2\dots k]$是$n − (k − 2)\dots n$。后一行$[i + 1][2\dots k]$是$x + 2\dots x + k$。
于是答案贡献为$n-k-x$。这部分总答案为$\sum_{j=1}^{n-k}n-j-k$,化简即为$\begin{pmatrix}n-k\\ 2\end{pmatrix}$。
注意$n=1$时答案应为$n-1$,这样算是错的。
设$g_{n,k}=\begin{pmatrix}n-k\\ 2\end{pmatrix}$,那么$f_{n,k}=g_{n,k}+\sum_{j=1}^{n-k+1}f_{n-j,k-1}$,相当于每次枚举第一行相同的值,将第一行去掉。$n-j$表示可取数的个数为$n-j$。
发现$\begin{pmatrix}n\\ m\end{pmatrix}$实际意义可以是将$n+1$有顺序地划分为$m+1$个正整数,那么考虑$g_{x,y}$贡献次数,从$f_{n,k}$到$g_{x,y}$迭代了$k-y$次,第一维一共减了$n-x$,那么它的次数应为将$n-x$划分为$k-y$个正整数的方案,即为$\begin{pmatrix}n-x-1\\ k-y-1\end{pmatrix}$。
考虑枚举$g_{x,y}$中的$y$,计算$2\leq y\leq x\leq k$的总贡献。
第一步枚举$x$相当于将$n − y + 1$划分为$n − x$和$x − y + 1$两个正整数,然后将$n-x$划分为$k-y$个正整数是它的系数,将$x-y+1$划分为$3$个正整数是它的值,合起来考虑,$y$的贡献即为将$n-y+1$划分为$k-y+3$个正整数的方案,也就是$\begin{pmatrix}n-y\\k-y-2\end{pmatrix}$。
因为迭代出的$f_{x,1}$要特殊处理,所以刚才枚举$y$从$2$开始。最后再将$f_{x,1}$的贡献加上即可。
$k=1$要特判。
$code:$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 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(); } 10 return x*f; 11 } 12 inline void write(int x,char sp){ 13 char ch[20]; int len=0; 14 if(x<0){ putchar('-'); x=~x+1; } 15 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int &x,int &y){ x^=y^=x^=y; } 21 inline void chmax(int &x,int y){ x=x<y?y:x; } 22 inline void chmin(int &x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 const int NN=1e6+5,p=1e9+7; 26 int n,k,m,fac[NN],inv[NN],ans; 27 inline int C(int x,int y){ return x<0||y<0||x<y?0:fac[x]*inv[y]%p*inv[x-y]%p; } 28 inline int qpow(int a,int b){ 29 int res=1; 30 while(b){ 31 if(b&1) res=res*a%p; 32 a=a*a%p; 33 b>>=1; 34 } 35 return res; 36 } 37 38 signed main(){ 39 freopen("perm.in","r",stdin); 40 freopen("perm.out","w",stdout); 41 n=read(); k=read(); m=read(); n=n-k+m; k=m; fac[0]=inv[0]=1; 42 43 for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%p; inv[n]=qpow(fac[n],p-2); 44 for(int i=n-1;i;i--) inv[i]=inv[i+1]*(i+1)%p; 45 46 if(k==1){ write(n-1,'\n'); return 0; } 47 for(int i=2;i<=k;i++) (ans+=C(n-i,k-i+2))%=p;//cout<<ans<<endl; 48 for(int i=2;i<=n;i++){ 49 int num=C(n-i-1,k-2); 50 int tmp=i-1; 51 (ans+=num*tmp%p)%=p;//cout<<i<<' '<<num<<' '<<tmp<<endl; 52 } 53 write(ans,'\n'); 54 return 0; 55 }
T4 小P的生成树
发现在最后加和的负数的方向向量确定的情况下,每条边的权值是固定的。于是
注意题解里算$tan$式子列反了。。
$code:$
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 namespace IO{ 5 inline int read(){ 6 char ch=getchar(); int x=0,f=1; 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(); } 9 return x*f; 10 } 11 inline void write(int x,char sp){ 12 char ch[20]; int len=0; 13 if(x<0){ putchar('-'); x=~x+1; } 14 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 15 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 16 } 17 inline int max(int x,int y){ return x<y?y:x; } 18 inline int min(int x,int y){ return x<y?x:y; } 19 inline void swap(int &x,int &y){ x^=y^=x^=y; } 20 inline void chmax(int &x,int y){ x=x<y?y:x; } 21 inline void chmin(int &x,int y){ x=x<y?x:y; } 22 } using namespace IO; 23 24 const int NN=210; 25 int n,m,f[NN]; 26 vector<double>arr; 27 double ans,aa,bb; 28 struct eg{ 29 int u,v,a,b; 30 double val; 31 bool operator<(const eg& x)const{ return val>x.val; } 32 }e[NN]; 33 int getf(int x){ return f[x]==x?x:f[x]=getf(f[x]); } 34 35 void kruscal(){ 36 sort(e+1,e+m+1); 37 aa=0; bb=0; 38 for(int i=1;i<=n;i++) f[i]=i; 39 for(int i=1;i<=m;i++){ 40 int fu=getf(e[i].u),fv=getf(e[i].v); 41 if(fu==fv) continue; 42 f[fv]=fu; 43 aa+=e[i].a; bb+=e[i].b; 44 } 45 ans=max(ans,sqrt(aa*aa+bb*bb)); 46 } 47 signed main(){ 48 freopen("mst.in","r",stdin); 49 freopen("mst.out","w",stdout); 50 n=read(); m=read(); 51 for(int i=1;i<=m;i++) 52 e[i].u=read(),e[i].v=read(),e[i].a=read(),e[i].b=read(); 53 for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) if(i!=j){ 54 double tn=1.0*(e[i].a-e[j].a)/(e[i].b-e[j].b); 55 double ar=atan(tn); 56 arr.push_back(ar), arr.push_back(ar+acos(-1)); 57 } 58 sort(arr.begin(),arr.end()); 59 for(int i=0;i<arr.size()-1;i++){ 60 int j=i+1; 61 double mid=(arr[i]+arr[j])/2; 62 for(int k=1;k<=m;k++) e[k].val=1.0*e[k].a*cos(mid)+1.0*e[k].b*sin(mid); 63 kruscal(); 64 } 65 printf("%.6lf",ans); 66 return 0; 67 }