• 模拟59 题解


    A. Reverse

    手玩第二个样例,发现可以dp。

    但是似乎没有显然的拓扑序,所以直接跑最短路。

    然而暴力建图$n^2$,解决方法是线段树优化建图。

    利用$bfs$每个点只在第一次被更新时最优的性质,直接用$set$或者链表维护也是可以的。

    B. Silhouette

    首先是一个显然的性质:

    因为不关注先后左右的相对位置,将$a$ $b$数组任意交换顺序,不会对答案造成影响。

    那么不妨将$a$,$b$分别排序。

    对于矩阵上每一个点$(i,j)$,设它的值为$min(a[i],b[j])$

    从大到小考虑每一个值,那么这个值必定形成矩形或一个$L$形。

    矩形可以当作没有棱角的$L$形考虑,只要考虑$L$形时的方案数就可以了。

    设矩形为$a$行$b$列,上面还有$c$行,右面还有$d$列。

    容易发现,如果上面有c行,那么这c行一定已经合法。

    如果右面有d列,那么这d列已经合法,所以无需进行容斥,直接统计答案就可以了。

    我们只要使a行b列的矩形合法。设$min(a[i],b[j])=x$。

    合法的条件是:每一行每一列存在$x$且不存在$>x$的值。

    设$f(i)$表示保证每一列合法,有$i$行不合法的方案数。

    $ans=sum limits_{i=0}^{a}inom{a}{i}(-1)^if(i)$

    $f(i)=(x^i*((x+1)^{a+c-i}-x^{a+c-i}))^b*(x^i*(x+1)^{c-i})^d$

    因为互不影响,将所有的ans累乘是最终答案。

    C. Seat

    很扯的一道题,下面是自己注释的std。

    很多东西搞的不是很懂,但是可以强行解释。

     1 //注意
     2 //本题中odd为偶数,even为奇数
     3 #include<bits/stdc++.h>
     4 using namespace std;
     5 const int N=1030;
     6 int n,mod;
     7 int qpow(int x,int k,int ans=1){
     8     while(k){
     9         if(k&1) ans=ans*x%mod;
    10         x=x*x%mod;
    11         k>>=1;
    12     }
    13     return ans;
    14 }
    15 //dp为答案
    16 //f为题解中dp数组
    17 int dp[N][N],f[N][N],g[N][N],vis[N],inv[N],cnt[N],odd[N],pos[N];
    18 int main(){
    19     scanf("%d%d",&n,&mod);
    20     for(int i=1;i<=n;++i) inv[i]=qpow(i,mod-2);
    21     vis[0]=vis[n+1]=true;
    22     for(int i=1;i<=n;++i){
    23         int pl=0,pr=0,mx;
    24         for(int j=0;j<=n;++j){
    25             int r=j+1;
    26             while(!vis[r]) ++r;
    27             if(r-j>pr-pl) pl=j,pr=r;
    28             j=r-1;
    29         }
    30         ++cnt[mx=pr-pl>>1]; odd[mx]+=pr-pl&1;
    31         pos[i]=pl+mx; vis[pl+mx]=true;
    32     }
    33     int sum=n;
    34     for(int i=1;i<=n;++i){
    35         if(!cnt[i]) continue;
    36         int l=sum-cnt[i]+1,r=sum;
    37         if(i==1) for(int j=l;j<=r;++j) for(int k=l;k<=r;++k) dp[j][pos[k]]=inv[cnt[i]];//直接赋值为距离为1个数的逆元? 或许这里也被平均掉了
    38         else{
    39             for(int j=0;j<=cnt[i];++j) for(int k=0;k<=odd[i];++k) f[j][k]=0;//清空数组
    40             f[0][odd[i]]=1; //还没有坐人,那么该层剩下odd[i]个偶数区间的概率为1
    41             int p=l+odd[i]-1; //偶数区间长度的最后一个人
    42             for(int j=1;j<=cnt[i];++j){
    43                 int oddw=0,evenw=0;//odd为偶数 even为奇数
    44                 for(int k=0;k<=odd[i];++k){
    45                     if(!f[j-1][k]) continue;//dp转移 所以f值为0的时候可以剪枝 k表示剩余多少个偶数区间
    46                     int frac=(cnt[i]-(j-1))+k,w=0; //括号内表示剩余的区间个数 +k表示剩余多少个转移点
    47                     if(k){//还有偶区间剩余
    48                         w=f[j-1][k]*k*2%mod*inv[frac]%mod;//占一个偶区间位置 那么概率为k*2/转移点
    49                         oddw=(oddw+w*inv[odd[i]*2])%mod; //方便累加答案 对于这一次的转移 可能作用在不同的转移点
    50                         (f[j][k-1]+=w)%=mod;
    51                     }
    52                     if(cnt[i]-odd[i]){//可以向奇区间转移
    53                         w=f[j-1][k]*(frac-2*k)%mod*inv[frac]%mod;//向奇数区间转移的概率
    54                         evenw=(evenw+w*inv[(cnt[i]+odd[i])-odd[i]*2])%mod;//向不同奇数区间转移
    55                         (f[j][k]+=w)%=mod;
    56                     }
    57                 }
    58                 for(int u=l;u<=p;++u) (dp[l+j-1][pos[u]]+=oddw)%=mod,(dp[l+j-1][pos[u]+1]+=oddw)%=mod;//累加答案
    59                 for(int u=p+1;u<=r;++u) (dp[l+j-1][pos[u]]+=evenw)%=mod;//这里存在疑问 似乎将偶数区间默认加给了前面的pos
    60             }
    61             for(int j=l;j<=p;++j){//
    62                 int L=pos[j]-i+1,R=pos[j]+i;//当前的偶区间左右端点
    63                 for(int v=L;v<=R;++v){
    64                     if(v==pos[j]) continue;//不能是选择的点
    65                     for(int u=r+1;u<=n;++u){//后面每一个人
    66                         int s=v<pos[j]?v+i+1:v-i,w=dp[u][v]*inv[2]%mod;//后一个人在位置v的概率除2
    67                         (g[u][v]+=w)%=mod; (g[u][s]+=w)%=mod;//平均一下 虽然不知道为啥 但是感觉很对
    68                     }
    69                 }
    70                 for(int v=L;v<=R;++v) for(int u=r+1;u<=n;++u) dp[u][v]=g[u][v],g[u][v]=0;
    71             }
    72         }
    73         sum-=cnt[i];//考虑下一层 剩余人数减少
    74     }
    75     for(int i=1;i<=n;i++,puts("")) for(int j=1;j<=n;j++) printf("%d ",dp[i][j]);
    76     return 0;
    77 }
  • 相关阅读:
    算法训练 表达式计算
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    New ways to verify that Multipath TCP works through your network
    TCP的拥塞控制 (Tahoe Reno NewReno SACK)
    Multipath TCP Port for Android 4.1.2
    How to enable ping response in windows 7?
    NS3
    Multipath TCP Port for Android
  • 原文地址:https://www.cnblogs.com/skyh/p/11623248.html
Copyright © 2020-2023  润新知