[考试反思]0305省选模拟38:存在
然而今天没有第二张图。 因为我并没有上榜。。。
离考试结束还有$10$分钟左右的时候开始尝试提交,然后断网了。
然后连了$10$分钟没连上,然后就硬核爆零了。
如果交上去的话,打的都打满了,$24+27+40=91$.是$rk9$
好不到哪里去。。反正是被这个网严重影响了心情。
$T1$大概想到了正解思路但是中途网断了,重连时候就没有按照原来的思路想下去。
$T2$一眼发现大概是原题,于是开始想,大概有印象但是具体内容忘掉了,所以打了暴力就走了。
$T3$想到了正解的一部分,但是因为时间不够以及网络问题没有继续深究,拿着比较丰厚的$40$暴力就走了。
然后这么简单的一次考试我就爆零了。。。
其实今天状态也不太好,有一点头晕&困。然后下午就集中爆发了。
大概在晚上恢复了状态,压着时间把三道题都改出来了(得亏题目简单。。。)
话说仨题两个前缀和一个差分,出题人想表达什么。。?
T1:inverse
大意:排列,$k$次操作等概率翻转一个区间。求最终逆序对数的期望。$n \le 500,k \le 50$
我转化成方案数来统计了,一样的。
设$dp_{i,j,k}$表示$i$轮后$P_j > P_k$的方案数。枚举翻转区间暴力来复杂度是$O(kn^4)$的
然后我们分情况讨论,讨论翻转的区间包含了哪个端点,4种情况,每种的转移点数量都是不超过$O(n)$的,所以复杂度是$O(kn^3)$
然后我们把转移式子继续化简,发现可以使用前缀和优化,还是二维的,所以多弄几个行,列,斜线的前缀和优化,时间复杂度就是$O(kn^2)$的了
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 1000000007 4 #define cl(s) memset(s,0,sizeof s) 5 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 6 int mo(int a){return a>=mod?a-mod:a;} 7 int px(int x){return x*(x+1)>>1;} 8 int n,k,p[555],dp[501][501],ans,s1[505][505],s2[505][505],s3[505][505],s4[505][505],s5[505][505],s6[505][505]; 9 int main(){ 10 scanf("%d%d",&n,&k); 11 for(int i=1;i<=n;++i)scanf("%d",&p[i]); 12 for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)dp[i][j]=p[i]>p[j]; 13 for(int o=1,pl=1;o<=k;++o){ 14 for(int i=1;i<=n;++i)for(int j=1;j<=n;++j) 15 s1[i][j]=mo(s1[i][j-1]+dp[i][j]),s2[i][j]=mo(s2[i][j-1]+s1[i][j]), 16 s3[i][j]=mo(s3[i-1][j]+dp[i][j]),s4[i][j]=mo(s4[i-1][j]+s3[i][j]), 17 s5[i][j]=mo(s5[i-1][j-1]+dp[i][j]),s6[i][j]=mo(s6[i-1][j-1]+s5[i][j]); 18 for(int i=1;i<=n;++i)for(int j=i+1;j<=n;++j) 19 dp[i][j]=dp[i][j]*(0ll+px(i-1)+px(j-i-1)+px(n-j))%mod, 20 dp[i][j]=(0ll+dp[i][j]+s4[j-1][j]-s4[j-i-1][j]-s4[i-1][j]+mod+mod)%mod, 21 dp[i][j]=(0ll+dp[i][j]+s2[i][n]+s2[i][i-1]-s2[i][n+i-j]-s2[i][j-1]+mod+mod)%mod, 22 dp[i][j]=(0ll+dp[i][j]+s6[n][n+i-j]-s6[n-i][n-j]-s6[j-1][i-1]+mod+mod)%mod; 23 pl=1ll*pl*px(n)%mod; 24 for(int i=1;i<=n;++i)for(int j=1;j<i;++j)dp[i][j]=mo(mod+pl-dp[j][i]); 25 } 26 for(int i=1;i<=n;++i)for(int j=i+1;j<=n;++j)ans=mo(ans+dp[i][j]); 27 printf("%lld\n",1ll*ans*qp(qp(px(n),k),mod-2)%mod); 28 }
T2:Subsequence
大意:数列$A$,对于所有长度$k \in [1,n]$求一个长为$k$的子序列$B$,最大化$\sum\limits_{i=1}^{k} i \times B_i$。$n \le 100000$
原题链接。
不一样的地方在于这次必须是子序列,不能乱序。所以把排序去掉就好了。
还是决策单调性,平衡树维护差分$dp$数组,依次插入每个数,然后是区间加之类的。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 123456 4 int n,c[2][S],f[S],sz[S],s[S],rt,pc;long long Ans,lz[S],w[S],a[S]; 5 #define lc c[0][p] 6 #define rc c[1][p] 7 void up(int p){sz[p]=sz[lc]+sz[rc]+1;} 8 void down(int p){lz[lc]+=lz[p];lz[rc]+=lz[p];w[lc]+=lz[p];w[rc]+=lz[p];lz[p]=0;} 9 void spin(int p){ 10 int F=f[p],G=f[F],d=c[1][F]==p,B=c[!d][p]; 11 if(G)c[c[1][G]==F][G]=p; c[d][c[!d][p]=F]=B; 12 if(B)f[B]=F; f[f[F]=p]=G; up(F); up(p); 13 } 14 void splay(int p){ 15 int F=p,t=0; while(F)s[++t]=F,F=f[F]; while(t)down(s[t--]); 16 for(;F=f[p];spin(p))if(f[F])spin(c[1][f[F]]==F^c[1][F]==p?p:F); rt=p; 17 } 18 void insert(int p){ 19 int lp,d,Sz=1; 20 while(p){ 21 down(lp=p); 22 if(w[p]>a[pc]*(Sz+sz[lc]))Sz+=sz[lc]+1,d=1; 23 else d=0;p=c[d][p]; 24 } 25 p=pc; c[d][f[p]=lp]=p; w[p]=a[p]*Sz; splay(p); 26 w[rc]+=a[p]; lz[rc]+=a[p]; 27 } 28 void dfs(int p){ 29 if(!p)return; down(p); 30 dfs(lc); Ans+=w[p]; printf("%lld ",Ans); dfs(rc); 31 } 32 int main(){ 33 scanf("%d%lld",&n,&a[1]); 34 rt=sz[1]=pc=1;w[1]=a[1]; 35 for(pc=2;pc<=n;++pc)scanf("%lld",&a[pc]),insert(rt); 36 dfs(rt); 37 }
T3:Convex
大意:凸多边形,求按照每条对角线划分后两部分面积差绝对值之和的2倍。$n \le 2 \times 10^6$
暴力的做法是枚举对角线嘛。
不那么暴力的话我们发现我们可以只枚举一个端点,然后我们可以找到一个分界点,顺时针方向都是对角线上方的比较大,逆时针则小。
判断的条件是面积是否超过整个多边形的一半。因为是凸多边形所以随着你枚举的点递增分界点也递增,单调指针就好了。
得到分界点,问题在于求出若干多边形面积的和。
考虑多边形面积如何计算:三角剖分。这道题里为了让不同的边之间没有关联,我们围绕原点进行三角剖分。
也就是说某个多边形面积等于它在原多边形上,直线与每对相邻的两点相连,得到的两个向量的叉积。
所以问题就转化到了相邻的点对上。发现如果当前枚举点为$x$而分界点是$y$那么$(0,0)-> y-1 \times (0,0) -> y$这对向量叉积累加答案一次。
$(y-2,y-1)$累加$2$次。。。$(x,x+1)$累加$y-x$次。是等差数列。所以维护前缀和的前缀和,加加减减就好了。
这是在原凸多边形上相邻的两个点做出的贡献,通过预处理解决了。
剩下没有考虑的就是对角线两点的贡献。发现当你枚举的点确定是,另一个点是连续的。
你大概要求一个$\sum\limits_{i=x}^{y} x_x y_i - y_x x_i$。很明显的前缀和形式,维护横纵坐标的前缀和就好了。
既然已经得到了分界点以及一堆面积小于一半的多边形的面积,那么剩下的就都可以大概同理的求出来了。
然后加一些取模优化啥的不然可能被卡常。。大概是没了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 6222222 4 #define mod 1000000007 5 long long s[S],s2[S],s1[S],X[S],Y[S],ans,ss,x[S],y[S];int n; 6 long long cross(int a,int b){return x[a]*y[b]-x[b]*y[a];} 7 long long cal(int a,int b){if(b>n)b-=n;return cross(a,b)+s[b-1]-s[a-1]+(a<b?0:s[n]);} 8 int read(){ 9 register char ch=getchar();register int p=0,f=0; 10 while(!isdigit(ch))f=ch=='-',ch=getchar(); 11 while(isdigit(ch))p=p*10+ch-48,ch=getchar(); 12 return f?-p:p; 13 } 14 int mo(int a){return a<0?a+mod:(a>=mod?a-mod:a);} 15 int main(){ 16 scanf("%d",&n); 17 for(int i=1;i<=n;++i)x[i]=read(),y[i]=read(),x[n+i]=x[i],y[n+i]=y[i]; 18 for(int i=1;i<=n;++i)s[i]=s[i-1]+cross(i+1,i); 19 for(int i=1;i<=n+n;++i)s1[i]=(s1[i-1]+cross(i+1,i))%mod,s2[i]=mo(s2[i-1]+s1[i]),X[i]=mo(X[i-1]+x[i]),Y[i]=mo(Y[i-1]+y[i]); 20 ss=s[n]%mod;int pt=3; 21 for(int i=1;i<=n;++i){ 22 while(cal(i,pt+1)<=s[n]>>1)pt++; 23 long long a1=mo((s2[pt-1]-s2[i-1]-s1[i-1]*(pt-i))%mod+mod),a2=mo((s2[i+n-3]-s2[i-1]-s1[i-1]*(n-2))%mod+mod); 24 a1+=x[i]*(Y[pt]-Y[i-1])-y[i]*(X[pt]-X[i-1]); 25 a2+=x[i]*(Y[i+n-2]-Y[i-1])-y[i]*(X[i+n-2]-X[i-1]); 26 a2-=a1; 27 ans=(ans+ss*(pt-i-1)-2*a1+2*a2-ss*(n-3-(pt-i-1)))%mod; 28 }printf("%lld\n",(ans+mod)%mod*500000004ll%mod); 29 }