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:$

2021.9.17考试总结[NOIP模拟55]
 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 }
T1

T2 String


神仙题,待补

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:$

2021.9.17考试总结[NOIP模拟55]
 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 }
T3

 T4 小P的生成树


发现在最后加和的负数的方向向量确定的情况下,每条边的权值是固定的。于是2021.9.17考试总结[NOIP模拟55]

 

注意题解里算$tan$式子列反了。。

$code:$

2021.9.17考试总结[NOIP模拟55]
 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 }
T4

 

上一篇:Latex常用数学公式整理——矩阵


下一篇:DW吃瓜课程——机器学习理论知识笔记(二)