欢迎使用皮肤 Geek|

Tsawke

园龄:2年7个月粉丝:6关注:2

2023.01.12 模拟赛小结

2023.01.12 模拟赛小结

更好的阅读体验戳此进入

赛时思路

T1

LG-P3978 [TJOI2015]概率论

求随机的 n 个点的二叉树的期望叶子节点个数。

最开始的思路是 dp(i,j,k) 表示前 i 个点,j 个叶子 k 个仅有一个子节点的点的期望。然后想了一个似乎较为显然的 3D0D 的 DP,不过假了,主要是因为一个子节点中左右是不固定的,这样对于方案数是错误的,一直没想到什么好方法,一个多小时以后才反应过来可以直接从枚举树的左右节点转移,这样可以考虑期望转计数,即:令 dp(i) 表示 i 个点所有方案叶子节点总数,tot(i) 表示方案数,转移枚举一下左右子树大小即可,然后打表发现后者是卡特兰数,前者是 dp(i)=i×tot(i1),后者的证明需要阴间拉格朗日反演,不会,所以考虑找规律即可,规律较显然。然后将 dp(i)tot(i) 化简一下即为 n2+n4n2,本来最后的式子都写出来了,但是因为输出的时候 n×4 想当然认为是 int 范围内,但是 4e9 炸掉了,所以 100pts70pts

关于具体的证明,准备在补完 ABC266Ex,写完可持久化文艺平衡树和序列加强版之后再去补生成函数相关内容。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;



template < typename T = int >
inline T read(void);

int N;
// ld dp[2][1100][1100];
// ll dp[1100];
// ll tot[1100];
// ld dp[1100];

int main(){
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
    N = read();
    // dp[1][1][0] = 1.0;
    // for(int i = 2; i <= N; ++i){
    //     ll base(0);
    //     for(int j = 0; j <= i; ++j)for(int k = 0; k <= i; ++k)if(dp[!(i & 1)][j][k])base += 2 * j + k;
    //     for(int j = 0; j <= i; ++j){
    //         for(int k = 0; k <= i; ++k){
    //             dp[i & 1][j][k] = 0.0;
    //             if(k - 1 >= 0)dp[i & 1][j][k] += (2.0 * j / base) * dp[!(i & 1)][j][k - 1],
    //             printf("upd i = %d, j = %d, k = %d, from %.5Lf\n", i, j, k, dp[!(i & 1)][j][k - 1]);
    //             if(j - 1 >= 0)dp[i & 1][j][k] += ((k + 1.0) / base) * dp[!(i & 1)][j - 1][k + 1];
    //         }
    //     }
    // }
    // ld ans(0.0);
    // for(int j = 0; j <= N; ++j)for(int k = 0; k <= N; ++k)ans += dp[N & 1][j][k] * j;
    // printf("%.9Lf\n", ans);

    // dp[1] = 1.0;
    // for(int i = 2; i <= N; ++i)
    //     for(int j = 0; j <= i; ++j)
    //         if(i - j - 1 >= 0)
    //             dp[i] += (dp[j] + dp[i - j - 1])
    // printf("%.9Lf\n", (ld)((__float128)dp[N] / tot[N]));

    // dp[1] = 1, tot[0] = tot[1] = 1;
    // for(int i = 2; i <= N; ++i)
    //     for(int j = 0; j <= i; ++j)
    //         if(i - j - 1 >= 0)
    //             dp[i] += dp[j] * tot[i - j - 1] + dp[i - j - 1] * tot[j],
    //             tot[i] += tot[j] * tot[i - j - 1];
    // for(int i = 0; i <= N; ++i)printf("dp is %lld, tot is %lld\n", dp[i], tot[i]);
    printf("%.10Lf\n", (((ld)N * N + N) / (4 * N - 2)));
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

T2

LG-P4211 [LNOI2014]LCA

给定 n 个节点的有根树,m 次询问给定 l,r,zi=lrdep(LCA(i,z))

以前没见过这种套路,想了一会能想到的只有离线下来 Tarjan 求 LCA,这样最终复杂度是 O(nm) 的,理论上在模拟赛的数据下能过 40pts,然后空间消耗巨大,根据数据需要 1GiB2GiB 左右(所以让 @sssmzy 调大了点空间

然后最后耗时 1030ms,刚好 TLE,甚至跑不过 @cc0000 的 O(nmlogn) 的树剖(因为 @sssmzy 的数据随机生成,所以树剖和纯暴力期望都是可以艹过去的)(当然最后又让 @sssmzy 开大了点时间

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;



template < typename T = int >
inline T read(void);

struct Edge{
    Edge* nxt;
    int to;
    OPNEW;
}ed[21000];
ROPNEW;
Edge* head[11000];

int N, M;
int cnt(0);
int dep[11000];
ll ans[11000];
struct Query{short p; int idx;};
basic_string < Query > qs[11000];
bitset < 11000 > vis;
int lca[110000000];
struct Q{int l, r, z;}q[11000];

class UnionFind{
private:
    int fa[11000];
public:
    UnionFind(void){for(int i = 1; i <= 10000; ++i)fa[i] = i;}
    int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
    void Union(int x, int y){fa[Find(y)] = Find(x);}
}uf;

void dfs(int p = 1, int fa = 0){
    dep[p] = dep[fa] + 1;
    for(auto i = head[p]; i; i = i->nxt)if(SON != fa)dfs(SON, p);
}
void Tarjan(int p = 1, int fa = 0){
    vis[p] = true;
    for(auto i = head[p]; i; i = i->nxt)if(SON != fa)Tarjan(SON, p), uf.Union(p, SON);
    for(auto q : qs[p])if(vis[q.p])lca[q.idx] = uf.Find(q.p);
}

int main(){
    freopen("eert.in", "r", stdin);
    freopen("eert.out", "w", stdout);
    N = read(), M = read();
    for(int i = 2; i <= N; ++i){
        int s = i, t = read();
        head[s] = new Edge{head[s], t};
        head[t] = new Edge{head[t], s};
    }dfs();
    for(int i = 1; i <= M; ++i){
        q[i].l = read(), q[i].r = read(), q[i].z = read();
        for(int p = q[i].l; p <= q[i].r; ++p)qs[q[i].z] += Query{(short)p, ++cnt}, qs[p] += Query{(short)q[i].z, cnt};
    }Tarjan();
    int cur(0);
    for(int i = 1; i <= M; ++i){
        for(int ccnt = cur + 1; ccnt <= cur + (q[i].r - q[i].l + 1); ++ccnt)ans[i] += dep[lca[ccnt]];
        cur += (q[i].r - q[i].l + 1);
    }
    for(int i = 1; i <= M; ++i)printf("%lld\n", ans[i]);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

T3

LG-P6621 [省选联考 2020 A 卷] 魔法商店

及其阴间的保序回归,经典 @sssmzy 组的模拟赛中的科技题,感觉暴力都不是很可写,跳了跳了。

正解

T1

上面提过了,4×n 没转 long long

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template < typename T = int >
inline T read(void);

int N;

int main(){
    N = read();
    printf("%.10Lf\n", (((ld)N * N + N) / (4ll * N - 2)));
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

T2

正解看到之后会发现非常显然,但是如果要直接想却不太容易想到,应该算是一种套路。

考虑对区间 [l,r] 中的点到根的路径 +1,此时对于询问的 z,若求其到根节点路径上的数值和即为所求。简单想一下不难理解。

不难想到 树剖 + 线段树 维护,这样的复杂度是 O(qnlog2n) 的,不能通过。

于是考虑将询问转成差分形式,即拆成 [1,l1][1,r] 的前缀询问,离线排序后挂在点上,从 1n 依次进行路径加,对应点进行询问即可,此时复杂度即可优化至 O(nlog2n),可以通过。

需要注意本题点的编号的问题。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define MOD (201314)

template < typename T = int >
inline T read(void);

struct Edge{
    Edge* nxt;
    int to;
    OPNEW;
}ed[110000];
ROPNEW;
Edge* head[51000];

int N, M;
struct Query{
    int endp;
    int val;
    int idx;
    bool flag; //false -> [1, l - 1], true -> [1, r]
};
basic_string < Query > qs;
ll ans[51000];

class SegTree{
private:
    ll tr[51000 << 2];
    ll lz[51000 << 2];
    #define LS (p << 1)
    #define RS (LS | 1)
    #define MID ((gl + gr) >> 1)
public:
    void Pushup(int p){tr[p] = (tr[LS] + tr[RS]) % MOD;}
    void Pushdown(int p, int gl, int gr){
        if(!lz[p])return;
        (lz[LS] += lz[p]) %= MOD, (lz[RS] += lz[p]) %= MOD;
        (tr[LS] += lz[p] * (MID - gl + 1) % MOD) %= MOD, (tr[RS] += lz[p] * (gr - (MID + 1) + 1) % MOD) %= MOD;
        lz[p] = 0;
    }
    ll Query(int l, int r, int p = 1, int gl = 1, int gr = N){
        if(l <= gl && gr <= r)return tr[p];
        if(gr < l || r < gl)return 0;
        Pushdown(p, gl, gr);
        return (Query(l, r, LS, gl, MID) + Query(l, r, RS, MID + 1, gr)) % MOD;
    }
    void Modify(int l, int r, int v = 1, int p = 1, int gl = 1, int gr = N){
        if(l <= gl && gr <= r)return (tr[p] += (gr - gl + 1) * v % MOD) %= MOD, (lz[p] += v) %= MOD, void();
        Pushdown(p, gl, gr);
        if(l <= MID)Modify(l, r, v, LS, gl, MID);
        if(MID + 1 <= r)Modify(l, r, v, RS, MID + 1, gr);
        Pushup(p);
    }
}st;

int dep[51000], dfn[51000], fa[51000], hson[51000], siz[51000], top[51000], idx[51000];

void dfs_pre(int p = 1, int ffa = 0){
    fa[p] = ffa;
    dep[p] = dep[ffa] + 1;
    siz[p] = 1;
    for(auto i = head[p]; i; i = i->nxt){
        if(SON == ffa)continue;
        dfs_pre(SON, p);
        siz[p] += siz[SON];
        if(siz[SON] > siz[hson[p]])hson[p] = SON;
    }
}
void dfs_make(int p = 1, int tp = 1){
    top[p] = tp;
    static int cdfn(0);
    dfn[p] = ++cdfn;
    idx[cdfn] = p;
    if(hson[p])dfs_make(hson[p], tp);
    for(auto i = head[p]; i; i = i->nxt){
        if(SON == fa[p] || SON == hson[p])continue;
        dfs_make(SON, SON);
    }
}
void ModifyTree(int p){
    while(top[p] != 1)
        st.Modify(dfn[top[p]], dfn[p]), p = fa[top[p]];
    st.Modify(1, dfn[p]);
}
ll QueryTree(int p){
    ll ret(0);
    while(top[p] != 1)
        (ret += st.Query(dfn[top[p]], dfn[p])) %= MOD, p = fa[top[p]];
    (ret += st.Query(1, dfn[p])) %= MOD;
    return ret;
}

int main(){
    N = read(), M = read();
    for(int i = 2; i <= N; ++i){
        int s = i, t = read() + 1;
        head[s] = new Edge{head[s], t};
        head[t] = new Edge{head[t], s};
    }dfs_pre(), dfs_make();
    for(int i = 1; i <= M; ++i){
        int l = read() + 1, r = read() + 1, p = read() + 1;
        qs += {Query{l - 1, p, i, false}, Query{r, p, i, true}};
    }sort(qs.begin(), qs.end(), [](const Query &a, const Query &b)->bool{return a.endp < b.endp;});
    int cur(0);
    for(auto q : qs){
        for(int i = cur + 1; i <= q.endp; ++i)ModifyTree(i);
        cur = q.endp;
        ans[q.idx] += (q.flag ? 1 : -1) * QueryTree(q.val);
    }
    for(int i = 1; i <= M; ++i)printf("%lld\n", (ans[i] % MOD + MOD) % MOD);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

T3

跳!

UPD

update-2023_01_12 初稿

本文作者:tsawke

本文链接:https://www.cnblogs.com/tsawke/p/17124329.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Tsawke  阅读(11)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起