• [考试反思]0402省选模拟60:空白


    一上午又跟划水一样。一个暴力两个空题。

    部分分又少又难写,真心难受的一场考试。

    空题倒不是不会,但是分别只会$15,10$分的档。于是博了博信仰尝试冲正解。

    然而当然什么都没有冲出来。

    也一直在弄$T1$想尝试$n^2$过五万或十万那样,然而依旧没卡过去。

    然后就惨成这样了。。。。。。

    也是状态不太好。冲$T3$其实差不多应该能想到,还是把太多时间放到$T1,2$上了。

    改题比较顺利。也许吧。今天可能是最后一次能学$OI$到这个时候了/

    T1:摧毁图状数

    大意:树,每次操作可以将一个点及其$1,2,...,k-1$辈祖先都点亮,对于所有$1 le k le n$求最少操作多少次能点亮整棵树。

    贪心的点叶子一定是对的。设叶子有$L$个。那么答案的上界是$O(L+frac{n}{k})$

    我们对于每个节点求出$a[i]$表示这个节点到子树里最近的叶子的距离。然后丢进$vector$里。

    可以发现除了叶节点,$a[i]=k$的点一定是最先被操作的。然后拿个优先队列依次操作,每次操作前判定这个点是否已经被点亮。

    所以弄一个$dfs$序+线段树。维护每个节点的子树内,被操作过的点的最小深度,与$dep[p]+k$比较一下即可。

    预处理叶子个数后。入队点个数是调和级数。所以总复杂度是$O(nlog^2n)$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 100005
     4 vector<int>s[S],v[S]; priority_queue<pair<int,int> >q;
     5 int n,dep[S],ans[S],L,mnd[S],f[19][S],dfn[S],tim,t[S<<2],dfr[S],Q[S],T;
     6 void pre(int p,int fa){
     7     dep[p]=dep[fa]+1; f[0][p]=fa; mnd[p]=n+1; dfn[p]=++tim;
     8     for(int i=1;i<19;++i)f[i][p]=f[i-1][f[i-1][p]];
     9     for(int i=0,y;y=i<s[p].size()?s[p][i]:0;++i)if(y!=fa)
    10         pre(y,p),mnd[p]=min(mnd[p],mnd[y]);
    11     dfr[p]=tim;
    12     if(mnd[p]==n+1)mnd[p]=dep[p],L++;v[mnd[p]-dep[p]].push_back(p);
    13 }
    14 int Fa(int p,int k){for(int i=18;~i;--i)if(k&1<<i)p=f[i][p];return p;}
    15 #define lc p<<1
    16 #define rc lc|1
    17 #define md (L+R>>1)
    18 void mdf(int x,int w,int p=1,int L=1,int R=n){
    19     if(L==R)return t[p]=w,void();
    20     if(x<=md)mdf(x,w,lc,L,md);else mdf(x,w,rc,md+1,R);
    21     t[p]=min(t[lc],t[rc]);
    22 }
    23 int ask(int l,int r,int p=1,int L=1,int R=n){
    24     if(l<=L&&R<=r)return t[p];
    25     return min(l<=md?ask(l,r,lc,L,md):n+n,r>md?ask(l,r,rc,md+1,R):n+n);
    26 }
    27 int main(){//freopen("tree2.in","r",stdin);
    28     cin>>n; memset(t,0x3f,sizeof t);
    29     for(int i=1,a,b;i<n;++i)scanf("%d%d",&a,&b),s[a].push_back(b),s[b].push_back(a);
    30     pre(1,0);
    31     for(int i=0;i<v[0].size();++i)mdf(dfn[v[0][i]],dep[v[0][i]]);
    32     for(int k=1;k<=n;++k){
    33         for(int i=0;i<v[k].size();++i)q.push(make_pair(dep[v[k][i]],v[k][i]));
    34         while(!q.empty()){
    35             int p=q.top().second,x;q.pop();
    36             if(ask(dfn[p],dfr[p])<dep[p]+k)continue;
    37             mdf(dfn[p],dep[p]); Q[++T]=dfn[p]; ans[k]++;
    38             if(x=Fa(p,k))q.push(make_pair(dep[x],x));
    39         }while(T)mdf(Q[T],n+n),T--;
    40     }int q,x;cin>>q;while(q--)scanf("%d",&x),printf("%d
    ",ans[min(n,x)]+L);
    41 }
    View Code

    T2:排列统计

    大意:一个长为$n$的排列,随机进行$k$次两元素交换,求最后的序列中$cnt$的期望。

    $cnt$定义为:从前往后扫,如果当前值是前缀最大值,$cnt++$。$n le 100,k le 80$

    枚举$x,y$表示最终序列中位置$x$上是数字$y$。

    设$dp[i][j][0/1/2/3/4]$表示交换了$i$次后$[1,x-1]$中有$j$个大于$y$的数。

    $0/2$表示$a[x]>y$。$1/3$表示$a[x]<y$。$4$表示$a[x]=y$

    设$a[p]=y$。$0/1$表示$p>x$。$2/3$表示$p<x$。显然$4$表示$p=x$

    然后就可以转移了,一共有$39$种转移。时间复杂度$O(39kn^3)$。常数很小。

    然而最大的测试点依旧跑了$3.4s$。最开始时限开$2s$有点过了。$8s$是不是有点多了。。?

    常数不重要,这题写出来已经不容易了。。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define mod 1000000007
     4 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;}
     5 int p[111],a[111],dp[2][111][5],ans,k,nw,nx=1,I,s,n;
     6 void go(int t,int j,int r){if(I+j>=0)dp[nx][I+j][t]=(dp[nx][I+j][t]+1ll*r*dp[nw][I][s])%mod;}
     7 int main(){
     8     cin>>n>>k; for(int i=1;i<=n;++i)scanf("%d",&a[i]),p[a[i]]=i;
     9     for(int x=1;x<=n;++x)for(int y=x;y<=n;++y){
    10         int c=0,R=min(x-1,n-y),r=1+(x-1)*(x-1)+(n-x)*(n-x);memset(dp,0,sizeof dp);
    11         for(int i=1;i<x;++i)c+=a[i]>y;
    12         dp[nw][c][(a[x]==y)<<2|(p[y]>x)<<1|a[x]>y]=1;
    13         for(int _=1;_<=k;++_,nw^=1,nx^=1)for(I=0;I<=R;++I){
    14             #define IF if(dp[nw][I][s])
    15             #define ls (LS-(s<2))
    16             #define rb (RB-(s&1))
    17             #define rs (n-x-rb-(s&2?1:0))
    18             int lb=I,LS=x-lb-1,RB=n-y-lb;
    19             s=4;IF go(0,0,ls*2),go(1,-1,lb*2),go(2,0,rs*2),go(3,0,rb*2);
    20             for(s=0;s<4;++s)IF go(4,s==1,2);
    21             for(s=0;s<4;s+=2)IF go(s,0,2*(ls+rs)),go(s^1,0,2*rb),go(s^1,-1,2*lb);
    22             for(s=1;s<4;s+=2)IF go(s,0,2*(lb+rb)),go(s^1,0,2*rs),go(s^1, 1,2*ls);
    23             for(s=0;s<2; ++s)IF go(s^2, 1,rb*2),go(s^2,0,rs*2);
    24             for(s=2;s<4; ++s)IF go(s^2,-1,lb*2),go(s^2,0,ls*2);
    25             for(s=0;s<5; ++s)IF go(s,0,ls*rs*2+lb*rb*2+r),go(s,1,ls*rb*2),go(s,-1,lb*rs*2),dp[nw][I][s]=0;
    26         }ans=(ans+dp[nw][0][4])%mod;
    27     }cout<<1ll*ans*qp(qp(n*n,k),mod-2)%mod<<endl;
    28 }
    View Code

    T3:归并排序

    大意:长度为$2$的整次幂的排列。执行正常的归并排序,然而在递归到长度为$2$的区间时会随机返回两种情况之一。

    支持操作:询问$x$最后出现在位置$y$的概率,以及支持交换原始序列中两个数的位置。$nle 2^{16},qle 10^5$

    不难发现,如果递归边界的两个数是$x,y(x<y)$

    那么排对了就相当于是$x,y$。排错了相当于是$y,y+0.5$

    所以每个数对应着两个可能的值,概率都是$frac{1}{2}$且互相独立。分开考虑贡献,期望有多少数比询问的数小,概率是多少。

    把每个数对应的两个数视为一个区间,询问的数视为两个点:

    要么整个区间都比点小,排名直接$+2$,要么大,排名不变,要么可大可小,直接组合数计算。

    树状数组维护左右端点的值,每次就相当于是求前后缀和了。时间复杂度$qlogn$

    被玄学了。同一份代码交两次结果不一样。迷茫。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 1<<18
     4 #define mod 1000000007
     5 int L[S],R[S],a[S],n,iv,fac[S],finv[S],q,y;
     6 void add(int*t,int p,int v){for(;p<=n*2+2;p+=p&-p)t[p]+=v;}
     7 int ask(int*t,int p,int a=0){for(;p;p^=p&-p)a+=t[p];return a;}
     8 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;}
     9 int C(int b,int t){return t<0||b<t?0:1ll*fac[b]*finv[t]%mod*finv[b-t]%mod;}
    10 void ctb(int x,int v=1){
    11     int y=a[x],z=a[x^1]; if(y>z)swap(y,z);
    12     add(R,z*2,v);add(R,z*2+1,v);
    13     add(L,n*2+2-y*2,v);add(L,2*n+2-z*2,v);
    14 }
    15 int cal(int x){
    16     int l=ask(R,x-1),r=ask(L,2*n+3-x);
    17     return 1ll*C(n-l-r,y-l)*qp(2,l)%mod*qp(2,r)%mod;
    18 }
    19 int main(){//freopen("10.in","r",stdin);freopen("0.out","w",stdout);
    20     cin>>n; iv=qp(qp(2,n+1),mod-2);
    21     for(int i=0;i<n;++i)scanf("%d",&a[i]);
    22     for(int i=0;i<n;i+=2)ctb(i);
    23     for(int i=fac[0]=1;i<=n;++i)fac[i]=1ll*fac[i-1]*i%mod;
    24     finv[n]=qp(fac[n],mod-2);
    25     for(int i=n-1;~i;--i)finv[i]=finv[i+1]*(i+1ll)%mod;
    26     scanf("%d",&q);
    27     for(int o,x;q--;){
    28         scanf("%d%d%d",&o,&x,&y);x--;y--;
    29         if(n==1&&o==2)puts(y?"0":"1");
    30         else if(o==1)ctb(x,-1),ctb(y,-1),swap(a[x],a[y]),ctb(x),ctb(y);
    31         else if(a[x]>a[x^1])printf("%lld
    ",2ll*cal(a[x]*2)*iv%mod);
    32         else printf("%lld
    ",1ll*iv*(cal(a[x]*2)+cal(a[x^1]*2+1))%mod);
    33     }
    34 }
    View Code
  • 相关阅读:
    基于Linux OpenJDK Debian的发行版的JAVA_HOME环境变量的正确目标是什么?
    redhat linux卸载默认的openjdk与安装sun的jdk
    更换介质:请把标有…… DVD 的盘片插入驱动器“/media/cdrom/”再按回车键“ 解决方法
    mysql 导出表结构和表数据 mysqldump用法
    转怎么让VI支持中文显示
    debian 更换sh的默认链接为bash
    基于percona-monitoring-plugins实现Zabbix的MySQL多端口自动发现监控
    elasticsearch中client.transport.sniff的使用方法和注意事项
    网络大数据分析 -- 使用 ElasticSearch + LogStash + Kibana 来可视化网络流量
    Parsing Netflow using Kibana via Logstash to ElasticSearch
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12623504.html
Copyright © 2020-2023  润新知