看到题目的时候,我的心情是这样的:
再次膜拜神犇NiroBC屠场爷。
T1
给你一个n*n的正方形矩阵,每次操作可以选择(n+1)/2*(n+1)/2的矩阵,将矩阵中的所有点权值变为其相反数,最大化矩阵点权和。
此题有一种似曾相识的即视感。
然而瞪了半天,依然只会打暴力。
发现有20分要用高斯消元,于是不断码码码,把一堆暴力码成一坨,结果自己看不懂了。。。
最后还是欢乐地拿了50分,因为数据里面有个全是0的彩蛋。
正解十分奇怪而且难写,在此不再赘述。
T2
给你一个排列,有m次操作,每次可以:
1、交换相邻的两个元素。
2、将序列完全随机打乱。
问采取最优策略操作完毕后的序列逆序对数的期望值。
感觉这道题非常可做,于是花了两个小时硬肛此题,结果喜闻乐见地爆蛋了。
令f[i][j]表示长度为i逆序对数为j的序列出现的概率,那么f[i][j]可以dp求出。
令F[x]为将序列打乱后,还有x次操作的期望答案,那么:
[tex]F[x]=\sum^{\frac{n(n-1)}{2}}_{i=1}min(F[x-1],max(0,i-x))*f[n][i][/tex]
显然不能枚举i,但是min(F[x-1],max(0,i-x))的取值是连续的,即随着i递增只会改变两次。
算出取值为F[x-1]、0、i-x时的i的范围,然后用前缀和维护即可。
T3
给你一个串S,回答以下两种询问:
1、给出k1、k2,求出S的不同子串中字典序为k1的子串的左右端点l,r;若该子串出现多次,求起始位置第k2小的子串的左右端点l,r。
2、给出l、r,求出左右端点为l、r的子串在S的不同子串中从小到大的字典序排名k1,以及在相同子串中从小到大起始位置的排名k2。
蒟蒻表示考的时候连题目都没看过。。。
考虑询问1,需要求出字典序为k1的子串,那么可以求出后缀数组和height,然后二分来求答案。
接着发现相同的子串在sa中必然是连续的。可以二分+rmq求出左右边界。
然后在这个范围内求出起始位置第k2小的子串,可以用主席树维护。
对于询问2,先知道了该子串的rank,然后就可以前后二分+rmq找到边界。
然后在主席树中求出比S[l,r]起始端点小的子串数就可以了!
【Codes】
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,m,i,j,cnt,ans; int a[110][110],equ[210][210],Equ[210][210],stx[10010],edx[10010],sty[10010],edy[10010]; int abs(int x){if (x<0)return -x;else return x;} void dfs(int k){ if (k>cnt){ int tot=0; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) tot+=a[i][j]; ans=max(ans,tot); return; } dfs(k+1); for (int i=stx[k];i<=edx[k];i++) for (int j=sty[k];j<=edy[k];j++)a[i][j]=-a[i][j]; dfs(k+1); for (int i=stx[k];i<=edx[k];i++) for (int j=sty[k];j<=edy[k];j++)a[i][j]=-a[i][j]; } int solve(){ int flag=0; for (int k=1;k<=n*n;k++) if (Equ[k][cnt+1]!=-1){ int l; for (l=1;l<=cnt+1;l++) if (Equ[k][l])break; if (l==cnt+1&&Equ[k][l]){flag=1;break;} for (int I=k+1;I<=n*n;I++) if (Equ[I][cnt+1]!=-1&&Equ[I][l]) for (int J=1;J<=cnt+1;J++)Equ[I][J]^=Equ[k][J]; } return flag^1; } int main(){ scanf("%d",&n); int m=n+1>>1; for (i=1;i<=n;i++) for (j=1;j<=n;j++){ scanf("%d",&a[i][j]); if (i+m-1<=n&&j+m-1<=n){cnt++;stx[cnt]=i;sty[cnt]=j;edx[cnt]=i+m-1;edy[cnt]=j+m-1;} } ans=-1000000007; if (n<=7)dfs(1); else if (n<=13){ for (i=1;i<=cnt;i++){ for (j=stx[i];j<=edx[i];j++) for (int k=sty[i];k<=edy[i];k++) equ[(j-1)*n+k][i]=1; } for (i=1;i<=n;i++) for (j=1;j<=n;j++) if (a[i][j]>0)equ[(i-1)*n+j][cnt+1]=0; else if (a[i][j]<0)equ[(i-1)*n+j][cnt+1]=1; else equ[(i-1)*n+j][cnt+1]=-1; int tot=0; for (i=1;i<=n;i++) for (j=1;j<=n;j++)tot+=abs(a[i][j]); for (i=1;i<=n*n;i++) for (j=1;j<=cnt+1;j++)Equ[i][j]=equ[i][j]; if (solve())ans=max(ans,tot); for (i=1;i<=n;i++) for (j=1;j<=n;j++) if (equ[(i-1)*n+j][cnt+1]!=-1){ for (int k=1;k<=n*n;k++) for (int l=1;l<=cnt+1;l++)Equ[k][l]=equ[k][l]; Equ[(i-1)*n+j][cnt+1]^=1; if (solve())ans=max(ans,tot-2*abs(a[i][j])); } }else{ for (i=0;i<=cnt;i++) for (j=0;j<=cnt;j++){ if (i&j&i==j)continue; if (i) for (int k=stx[i];k<=edx[i];k++) for (int l=sty[i];l<=edy[i];l++)a[k][l]=-a[k][l]; if (j) for (int k=stx[j];k<=edx[j];k++) for (int l=sty[j];l<=edy[j];l++)a[k][l]=-a[k][l]; int tot=0; for (int k=1;k<=n;k++) for (int l=1;l<=n;l++)tot+=a[k][l]; ans=max(ans,tot); if (i) for (int k=stx[i];k<=edx[i];k++) for (int l=sty[i];l<=edy[i];l++)a[k][l]=-a[k][l]; if (j) for (int k=stx[j];k<=edx[j];k++) for (int l=sty[j];l<=edy[j];l++)a[k][l]=-a[k][l]; } } printf("%d\n",ans); }
#include<cstdio> #include<algorithm> #include<cstring> typedef long long ll; using namespace std; const int M=50010; int cas,n,i,j; ll m; double f[110][M],sum[M],Sum[M],F[M],a[M]; int main(){ f[1][0]=1; for (i=2;i<=100;i++){ for (j=0;j<=(i-1)*(i-2)/2;j++)sum[j]=f[i-1][j]; for (j=1;j<=(i-1)*(i-2)/2;j++)sum[j]+=sum[j-1]; for (j=0;j<=i-1;j++)f[i][j]=sum[j]; for (j=i;j<=i*(i-1)/2-i+1;j++)f[i][j]=sum[j]-sum[j-i]; for (j=i*(i-1)/2-i+2;j<=i*(i-1)/2;j++)f[i][j]=sum[i*(i-1)/2-j]; for (j=0;j<=i*(i-1)/2;j++)f[i][j]=f[i][j]/i; } scanf("%d",&cas); while (cas--){ scanf("%d%lld",&n,&m); if (m>n*(n-1)/2)m=n*(n-1)/2; for (i=0;i<=n*(n-1)/2;i++)sum[i]=f[n][i],Sum[i]=i*f[n][i]; for (i=1;i<=n*(n-1)/2;i++)sum[i]+=sum[i-1],Sum[i]+=Sum[i-1]; F[0]=(double)n*(n-1)/4; for (i=1;i<=m;i++){ int now=(int)F[i-1]+i; F[i]=Sum[now]-Sum[i]-i*(sum[now]-sum[i])+F[i-1]*(sum[n*(n-1)/2]-sum[now]); } for (i=1;i<=n;i++)scanf("%d",&a[i]); int tot=0; for (i=1;i<=n;i++) for (j=i+1;j<=n;j++)if (a[i]>a[j])tot++; if (m==0)printf("%.12lf\n",(double)tot); else printf("%.12lf\n",min((double)max(tot-(int)m,0),F[m-1])); } }
/*━━━━━━神兽出没━━━━━━ * ┏┓ ┏┓ * ┏┛┻━━━┛┻┓ * ┃ ┃ * ┃ ━ ┃ * ┃ ┳┛ ┗┳ ┃ * ┃ ┃ * ┃ ┻ ┃ * ┃ ┃ * ┗━┓ ┏━┛Code is far away from bug with the animal protecting * ┃ ┃ 神兽保佑,代码无bug * ┃ ┃ * ┃ ┗━━━┓ * ┃ ┣┓ * ┃ ┏┛ * ┗┓┓┏━┳┓┏┛ * ┃┫┫ ┃┫┫ * ┗┻┛ ┗┻┛ * * ━━━━━━感觉萌萌哒━━━━━━ */ /** * ┏┓ ┏┓ * ┏┛┻━━━┛┻┓ * ┃ ┃ * ┃ ━ ┃ * ┃ > < ┃ * ┃ ┃ * ┃ ... O ... ┃ * ┃ ┃ * ┗━┓ ┏━┛ * ┃ ┃ Code is far away from bug with the animal protecting * ┃ ┃ 神兽保佑,代码无bug * ┃ ┃ * ┃ ┃ * ┃ ┃ * ┃ ┃ * ┃ ┗━━━┓ * ┃ ┣┓ * ┃ ┏┛ * ┗┓┓┏━┳┓┏┛ * ┃┫┫ ┃┫┫ * ┗┻┛ ┗┻┛ */ #include<cstdio> #include<algorithm> #include<cstring> typedef long long ll; using namespace std; const int M=500010; int n,m,i,j,opt,left,right,cnt; char S[M]; int s[M],wa[M],wb[M],wc[M],wd[M],sa[M],rank[M],height[M][30],log[M],root[M]; ll k1,k2,sum[M]; struct node{int l,r,w;}tree[M*20]; bool cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];} void getsa(int *r,int *sa,int n,int m){ int i,j,p,*x=wa,*y=wb; for (i=0;i<n;i++)wc[i]=0; for (i=0;i<n;i++)wc[x[i]=r[i]]++; for (i=1;i<m;i++)wc[i]+=wc[i-1]; for (i=n-1;i>=0;i--)sa[--wc[x[i]]]=i; for (j=1,p=1;p<n;j*=2,m=p){ p=0; for (i=n-j;i<n;i++)y[p++]=i; for (i=0;i<n;i++)if (sa[i]>=j)y[p++]=sa[i]-j; for (i=0;i<n;i++)wd[i]=x[y[i]]; for (i=0;i<m;i++)wc[i]=0; for (i=0;i<n;i++)wc[wd[i]]++; for (i=1;i<m;i++)wc[i]+=wc[i-1]; for (i=n-1;i>=0;i--)sa[--wc[wd[i]]]=y[i]; swap(x,y),p=1,x[sa[0]]=0; for (i=1;i<n;i++)x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } } void getheight(int *r,int *sa,int n){ int i,j,k=0; for (i=1;i<=n;i++)rank[sa[i]]=i; for (i=0;i<n;height[rank[i++]][0]=k){ if (k)k--; j=sa[rank[i]-1]; while (r[i+k]==r[j+k])k++; } } int rmq(int l,int r){ if (l>r)return 100000000; int p=log[r-l+1]; return min(height[l][p],height[r-(1<<p)+1][p]); } void update(int x,int l,int r,int &p){ tree[++cnt]=tree[p];p=cnt;tree[p].w++; if (l==r)return; int mid=l+r>>1; if (x<=mid)update(x,l,mid,tree[p].l); else update(x,mid+1,r,tree[p].r); } int query(int x,int y,int l,int r,int k){ if (l==r)return l; int t=tree[tree[y].l].w-tree[tree[x].l].w,mid=l+r>>1; if (t>=k)return query(tree[x].l,tree[y].l,l,mid,k); else return query(tree[x].r,tree[y].r,mid+1,r,k-t); } int Query(int x,int y,int l,int r,int L,int R){ if (l==L&&r==R)return tree[y].w-tree[x].w; int mid=l+r>>1; if (R<=mid)return Query(tree[x].l,tree[y].l,l,mid,L,R); else if (L>mid)return Query(tree[x].r,tree[y].r,mid+1,r,L,R); else return Query(tree[x].l,tree[y].l,l,mid,L,mid)+Query(tree[x].r,tree[y].r,mid+1,r,mid+1,R); } int main(){ scanf("%s",S); n=strlen(S); for (i=0;i<n;i++)s[i]=S[i]-'a'+1; s[n]=0; getsa(s,sa,n+1,27); getheight(s,sa,n); for (i=0;i<=n;i++)sum[i]=n-height[i][0]-sa[i]; for (i=1;i<=n;i++)sum[i]+=sum[i-1]; sa[0]=0; for (i=1;i<=n;i++)sa[i]++; for (i=1;i<=n;i++)s[i]=rank[i-1]; rank[0]=0;for (i=1;i<=n;i++)rank[i]=s[i]; for (i=1;i<=20;i++) for (j=1;j<=n-(1<<i)+1;j++) height[j][i]=min(height[j][i-1],height[j+(1<<(i-1))][i-1]); log[1]=0;for (i=2;i<=n;i++)log[i]=log[i/2]+1; for (i=1;i<=n;i++){ root[i]=root[i-1]; update(sa[i],0,n,root[i]); } scanf("%d",&m); while (m--){ scanf("%d",&opt); if (opt==1){ scanf("%lld%lld",&k1,&k2); int l=1,r=n; while (l<r){ int mid=l+r>>1; if (k1<=sum[mid])r=mid;else l=mid+1; } int left=sa[l],right=sa[l]+height[l][0]+k1-sum[l-1]-1; int L=l,R=n; while (L<R){ int mid=L+R+1>>1; if (rmq(l+1,mid)>=right-left+1)L=mid;else R=mid-1; } l=query(root[l-1],root[L],0,n,k2); printf("%d %d\n",l,l+right-left); }else{ scanf("%d%d",&left,&right); int l=1,r=rank[left]; while (l<r){ int mid=l+r>>1; if (rmq(mid+1,rank[left])>=right-left+1)r=mid;else l=mid+1; } int L=rank[left],R=n; while (L<R){ int mid=L+R+1>>1; if (rmq(rank[left]+1,mid)>=right-left+1)L=mid;else R=mid-1; } printf("%lld %d\n",sum[l-1]+right-left+1-height[l][0],Query(root[l-1],root[L],0,n,1,left)); } } }