2020 省选模拟测试 Round #12 solution (20/02/18)
【比赛链接】http://59.61.75.5:8018/contest/222
A. 问题求解
【题解】
类欧几里得板子题.
考虑 $m$ 二进制下每一位的贡献,有经典公式 $im$ 的第 $x$ 位为 $\lfloor\frac{im}{2^x}\rfloor-2\lfloor\frac{im}{2^{x+1}}\rfloor$. 类欧几里得即可.
【代码】
1 #include<bits/stdc++.h> 2 const int mod=1000000007; 3 long long n,m,ans[50],Ans; 4 inline long long LikeGcd ( long long a,long long b,long long c,long long n ) 5 { 6 long long nx=n%mod,v=b/c%mod*(nx+1)%mod; 7 if ( !a ) return v; 8 if ( a>=c ) return (LikeGcd(a%c,b%c,c,n)+v+nx*(nx+1)/2%mod*((a/c)%mod))%mod; 9 long long T=((__int128)a*n+b)/c-1; 10 return ((T+1)%mod*nx%mod-LikeGcd(c,c-b-1,a,T)+mod)%mod; 11 } 12 signed main() 13 { 14 scanf("%lld%lld",&n,&m); 15 for ( long long w=0;(1LL<<w)<=(m<<1);w++ ) ans[w]=LikeGcd(m,0,1LL<<w,n); 16 for ( long long w=0;(1LL<<w)<=m;w++ ) if ( (m>>w)&1 ) Ans=(Ans+(ans[w]-2*ans[w+1]%mod+mod)%mod*((1LL<<w)%mod))%mod; 17 return !printf("%lld\n",Ans); 18 }
B. 子串
【题解】
考虑 hash 判断. 设第 $i$ 位的字母的后一个位置为 $nxt_i$,权值为 $nxt_i-i$,即可 hash.
用可持久化线段树维护 hash 然后直接将所有后缀排序即可. 则答案为 $\frac{n(n+1}{2}-\sum\limits_{i=1}^{n-1} lcp(s_i,s_{i+1})$.
效率 $O(5 n \log^3 n)$. 如果常数非常优秀能过(注意不同二分方式造成的巨大常数差). 卡卡常就过了.
考虑优化,用可持久化块状数组维护即可. 效率 $O(5 n \log n \sqrt n)$.
【代码】
1 #include<bits/stdc++.h> 2 inline int read ( void ) 3 { 4 int x=0;char ch; 5 while ( !isdigit(ch=getchar()) ) ; 6 for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48); 7 return x; 8 } 9 #define maxn 50010 10 #define Hash 1000000007 11 #define HASH unsigned long long 12 struct tree { int ls,rs;HASH val; } t[maxn*30]; 13 int n,a[maxn],root[maxn],p[maxn],nxt[maxn],tot; 14 HASH Pow[maxn];std::set<int> s[maxn]; 15 inline void modify ( int &k,int fr,int l,int r,int p,HASH w ) 16 { 17 t[k=++tot]=t[fr];t[k].val+=w; 18 if ( l==r ) return;int mid=(l+r)>>1; 19 if ( p<=mid ) modify(t[k].ls,t[fr].ls,l,mid,p,w); 20 else modify(t[k].rs,t[fr].rs,mid+1,r,p,w); 21 } 22 inline HASH query ( int k,int l,int r,int ql,int qr ) 23 { 24 if ( !k or ( ql<=l and r<=qr ) ) return t[k].val; 25 HASH res=0;int mid=(l+r)>>1; 26 if ( ql<=mid and t[k].ls ) res+=query(t[k].ls,l,mid,ql,qr); 27 if ( qr>mid and t[k].rs ) res+=query(t[k].rs,mid+1,r,ql,qr); 28 return res; 29 } 30 std::unordered_map<int,HASH> map[maxn]; 31 inline HASH Q ( int l,int len ) 32 { 33 if ( map[l].count(len) ) return map[l][len]; 34 return map[l][len]=query(root[l],1,n,l,l+len-1)*Pow[l]; 35 } 36 inline int lcp ( int x,int y ) 37 { 38 int l=0,r=n,ans=0,max=n-std::max(x,y)+1; 39 while ( l<=r ) 40 { 41 int mid=(l+r)>>1; 42 if ( mid>max ) { r=mid-1;continue; } 43 if ( Q(x,mid)==Q(y,mid) ) l=mid+1,ans=mid; 44 else r=mid-1; 45 } 46 return ans; 47 } 48 int tmp[maxn]; 49 inline bool cmp ( int x,int y ) 50 { 51 int l=lcp(x,y); 52 if ( x+l>n ) return true; 53 if ( y+l>n ) return false; 54 return (*s[a[x+l]].lower_bound(x))-x<(*s[a[y+l]].lower_bound(y))-y; 55 } 56 inline void solve_sort ( int l,int r ) 57 { 58 if ( l==r ) return; 59 int mid=(l+r)>>1; 60 solve_sort(l,mid);solve_sort(mid+1,r); 61 int pos=l-1; 62 for ( int pos1=l,pos2=mid+1;pos<r; ) 63 if ( pos2==r+1 ) tmp[++pos]=p[pos1],pos1++; 64 else if ( pos1==mid+1 ) tmp[++pos]=p[pos2],pos2++; 65 else if ( !cmp(p[pos2],p[pos1]) ) tmp[++pos]=p[pos1],pos1++; 66 else tmp[++pos]=p[pos2],pos2++; 67 for ( int i=l;i<=r;i++ ) p[i]=tmp[i]; 68 } 69 signed main() 70 { 71 Pow[0]=1; 72 for ( int i=1;i<=50000;i++ ) Pow[i]=Pow[i-1]*Hash; 73 while ( ~scanf("%d",&n) ) 74 { 75 tot=root[n+1]=0; 76 for ( int i=1;i<=n;i++ ) s[a[i]=read()].insert(i),p[i]=i; 77 for ( int i=n;i;nxt[a[i]]=i,i-- ) 78 if ( nxt[a[i]] ) modify(root[i],root[i+1],1,n,nxt[a[i]],(nxt[a[i]]-i)*Pow[n-i+1]); 79 else root[i]=root[i+1]; 80 solve_sort(1,n); 81 long long ans=1LL*n*(n+1)/2; 82 for ( int i=1;i<n;i++ ) ans-=lcp(p[i],p[i+1]); 83 printf("%lld\n",ans); 84 for ( int i=1;i<=n;i++ ) map[i].clear(),s[i].clear(),nxt[i]=0; 85 } 86 return 0; 87 }
C. 内凸包
【题解】
显然枚举顶点. 这里枚举内凸包的左下角 $P$. 考虑 $dp$. 设 $f[i][j]$ 表示最后一条边为 $(i,j)$ 的答案. 考虑转移:
当且仅当前一条边 $(j,k)$ 与 $(i,j)$ 围成为凸的且新增加的 $\triangle PIJ$ 里面没有点时可以转移.
显然转移的条件可以用三角前缀和的方式优化(三角形面积),注意处理细节. 即可做到 $O(1)$ 判断. $O(n^3)$ 枚举 $i,j,k$ 转移即可.
效率 $O(Tn^4)$. 注意写的常数即可通过.
观察转移的性质,显然转移是连续的(画图可以发现),因此可以倒序枚举 $j,k$,同时往前跳,效率优化至 $O(n^3)$.
【代码】
1 #include<bits/stdc++.h> 2 inline int read ( void ) 3 { 4 int x=0;char ch;bool f=true; 5 while ( !isdigit(ch=getchar()) ) if ( ch=='-' ) f=false; 6 for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48); 7 return f ? x : -x ; 8 } 9 struct Vector 10 { 11 int x,y,id;double k; 12 Vector(int _x=0,int _y=0){x=_x;y=_y;} 13 inline friend Vector operator - ( const Vector &u,const Vector &v ) { return Vector(u.x-v.x,u.y-v.y); } 14 inline friend int operator * ( const Vector &u,const Vector &v ) { return u.x*v.y-u.y*v.x; } 15 inline int length ( void ) { return x*x+y*y; } 16 } p[60],q[60],P; 17 signed main() 18 { 19 for ( int T=read();T--; ) 20 { 21 int n=read(),ans=0; 22 for ( int i=1;i<=n;i++ ) p[i].x=read(),p[i].y=read(); 23 std::sort(p+1,p+n+1,[](const Vector &u,const Vector &v){return u.y==v.y ? u.x<v.x : u.y<v.y;}); 24 for ( int st=1;st<=n;st++ ) 25 { 26 int m=0;P=p[st]; 27 for ( int i=st+1;i<=n;i++ ) q[++m]=p[i]; 28 std::sort(q+1,q+m+1,[&](const Vector &u,const Vector &v){return (u-P)*(v-P)==0 ? (u-P).length()<(v-P).length() : (u-P)*(v-P)>0 ;}); 29 int f[60][60]={0}; 30 for ( int i=1;i<=m;i++ ) 31 { 32 int j=i-1; 33 while ( j and (q[i]-P)*(q[j]-P)==0 ) j--; 34 bool flag=(j==i-1); 35 while ( j ) 36 { 37 int k=j-1; 38 while ( k and (q[i]-q[j])*(q[k]-q[j])<0 ) k--; 39 int s=abs((q[i]-P)*(q[j]-P)); 40 if ( k ) s+=f[j][k]; 41 if ( flag ) f[i][j]=s; 42 ans=std::max(ans,s);j=k; 43 } 44 if ( flag ) for ( int j=2;j<=i;j++ ) f[i][j]=std::max(f[i][j],f[i][j-1]); 45 } 46 } 47 printf("%.1lf\n",ans*0.5); 48 } 49 return 0; 50 }