1015&&1017模拟赛订正题解

1015 

反正这两次的模拟赛都不太会写吧 感觉越来越dl 今天初赛 真好

T1 

反正题目给你excatalan 提醒你是卡特兰数了 m=0 的情况 你发现就是卡特兰

那么考虑 m为任意数字的 方法 显然 我们需要知道卡特兰数的证明方法 其实昨天探讨的时候 想了更多方法 有必要解决这样的问题

首先证明方法有折线法 由折线法 我们不妨引出 另外一种证明思想

卡特兰数对应的 问题模型都是 在第K次执行 操作2的时候 操作1都是至少执行了K次 那么我们用x轴表示 当前的操作次数 一共需要2n次

然后把操作1当做 向上45走根号2步 操作2当成 向下45走根号2步 那么此时我们就能发现一条到达 (2n,0) 的折线 

那么合法的方案数我们也能发现也就是 这个折线没有任意时刻 跨过x轴 那么我们考虑这个时候 用所有的方案数 减去 不合法的方案数

此时合法括号序列恰好对应我们寻找合法路径的方案数 那么所有的方案数对应$\binom{2n}{n}$

那么此时来考虑 怎么求所有不合法的方案数 我们记得 我们在求从(0,0)出发 向上 或者 向右 到达(n,n)

不跨过y=x 的方案数 我们是怎么求证的呢 我们画个图来探讨一下 我真不想画 截yxc神仙的b站上的 组合数学讲解

现在任意存在一个 不合法 的路径 那么我们找到第一次 跨过 这个直线的部分 到达另外一条直线 也就是图上 绿色 部分

我们此时将从这个点到后面的路径全部关于这个直线翻折过去

那么此时终点变成了 (n-1 n+1)那么所有不合法的路径就对应 所有从(0,0) 到达(n-1,n+1) 所有路径条数 那么方案数我们就显然知道了

此时我们回到上面的折线法 我们考虑 每次45度 这名方法 也是把第一次 跨过x轴 到达y=-1 这个直线的时候 K 之后K+1~2n 之间的路径 全部关于 y=-1 对称过来 

不过是把y=x 这个基准线 变成x轴 然后把绿色的线 

那么 终点变成了 (2n,-2) 此时操作1 比操作 少2 那么操作1 有n-1 操作2 有n+1 那么考虑 此时 就是2n 中选择 n-1 种的方案数  $\binom{2n}{n-1}$

那么不合法的方案数 也就是 至少一次不匹配的方案数 此时卡特兰数的 我们就证明了出来 有必要把这种翻折 寻找基准线的方法 理解一下。

此时$\binom{2n}{n}-\binom{2n}{n-1}$ 就是方案数  其实从这个时候 我们不妨思考一下 我们减去的就是对应的 至少一种不匹配的方案数 

那么 我们考虑 如果是恰好m次不合法的方案数 那么 我们 可以转化成 至少m次不匹配的方案数 - 至少m+1次不匹配的方案数 其实对应到折线图上

我们此时把基准线变成y=-m 这个时候 我们求出来 按照 上述的方案数 此时 我们求出来 就是 至少m次 不合法的方案数

此时 cat(n,m)-cat(n,m+1) 此时 就是答案了 cat(n,m)=$\binom{2n}{n}-\binom{2n}{n-m-1} $

注意取模 问题就解决了 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &x) {
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
} 
const ll mod=998244353;
ll fac[2002002],inv[2002002],n,m,ans;
inline ll pow(ll a,ll b) {
    ll res=1;
    while(b) {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
inline ll C(ll n,ll m) {
    if(m>n) return 0;
    if(m<0) return 0;
    if(n<0) return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
inline ll c(ll n) {return (C(2ll*n,n)-C(2ll*n,n+1)+mod)%mod;}
ll exctl(int n,int m) {return (C(2ll*n,n)-C(2*n,n-m-1)+mod)%mod;}
int main() {
    freopen("excatalan.in","r",stdin);
    freopen("excatalan.out","w",stdout);
    read(n); read(m); 
    fac[0]=1;
    for(int i=1;i<=2*n;i++) {
        fac[i]=fac[i-1]*i;
        fac[i]%=mod;
    }
    inv[2*n]=pow(fac[2*n],mod-2);
    for(int i=2*n-1;i>=0;i--) {
        inv[i]=inv[i+1]*(i+1);
        inv[i]%=mod;
    }
    if(m==0) ans=c(n);
    else ans=(exctl(n,m)-exctl(n,m-1)+mod)%mod;
    printf("%lld\n",ans);
    return 0;
}
View Code

T2 啥东西 我也不清楚 但是也不会

那就是考虑 求连通块之积的组合意义的话 那么就是 对于每一个连通块 选取一个点 的方案数 那么记 g[i] 表示以 i 为 根 并且i所在的连通块中选择一个点的方案数

那么 f[i] 表示 以 i 为根 并且 i 所在的连通块中 没有选择一个点的方案数 此时转移即可 复杂度O(n)

自我感觉 这种模型 我没有理解 回来会补可能qwq

代码 找不到了

T3 不会写 也没补

自我检讨一下 最近比较懒 很多东西没有订正

1017 

是个期望 比较容易推导 做法很多 期望dp啥的都很容易写

$f[i]$ 表示当前桌上牌有 i 张 然后抽到相同的期望步数 那么$f_n=1$ 然后转移就用概率*贡献即可 每次有 i/n 的概率 抽到以前的 有 n-i/n 的概率抽到一张新的 此时步数(贡献)就是$f_[i+1]+1$

//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define INF 2147483646
#define ll long long
#define db double
#define pii pair<ll,ll>
#define mk make_pair
#define us unsigned
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define mod 998244353ll
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=10000010;
ll N=10000001,Q;
ll f[MAXN],n;//f[i]表示已经摸出了i张牌还需要摸的牌数
ll jc,in[MAXN];
int inv[MAXN];
inline ll ksm(ll b,ll p)
{
    ll ans=1;
    while(p)
    {
        if(p&1)ans=(ans*b)%mod;
        b=b*b%mod;
        p=p>>1;
    }
    return ans;
}
inline void prepare()
{
    jc=1;
    for(int i=1;i<=N;++i)jc=jc*i%mod;
    inv[N]=ksm(jc,mod-2);
    for(int i=N-1;i>=0;--i)inv[i]=((ll)inv[i+1])*(i+1)%mod;
    jc=1;
    for(int i=1;i<=N;++i)
    {
        in[i]=(inv[i]*jc)%mod;
        jc=jc*i%mod;
    }
}
signed main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    Q=read();prepare();
    while(Q--)
    {
        n=read();f[n]=1;
        for(int i=n-1;i>=0;--i)
        {
            f[i]=((i*in[n])%mod+(((n-i)*in[n])%mod)*(f[i+1]+1))%mod;
        }
        printf("%lld\n",f[0]);
    }
    return 0;
}
View Code

然后优化一下 就是 (n)求逆元 而不是(3n) 偷队友的图

 

 

 

 

T2 反正就是个线段树离线

随便想一想就知道是离线做 但是我感觉比较难搞的还是知道要把dis+k 之后再排序 因为直接整体动态平移是比较难做的 

首先按dfs序建立线段树,将所有点的dis[i]与所有询问 的dis[x] + k拿出来从大到小排序,然后逐个考虑,对于一个dis[i],在线段树 中i对应的位置赋为dep[i],

对于每个dis[x] + k,求出x的子树区间的dep的最 小值即可得到答案。

其实 这个偏序问题 源神给了 一个CDQ 的做法 是可的 

这里写了一个线段树 代码好像搞丢了 回来找一下

T3 烧掉脑子 只会写前6个点 不想学莫比乌斯 我还记得在海亮被反演支配脑子的几天 

 

posted @ 2019-10-19 08:10  Tyouchie  阅读(134)  评论(0编辑  收藏  举报