[考试反思]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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

posted @ 2020-03-05 23:20  DeepinC  阅读(217)  评论(0编辑  收藏  举报