【LOJ】#3044. 「ZJOI2019」Minimax 搜索

LOJ#3044. 「ZJOI2019」Minimax 搜索

一个菜鸡的50pts暴力

\(dp[u][j]\)表示\(u\)\(j\)次操作能使得\(u\)的大小改变的方案数

设每个点的初始答案是\(S[u]\)

每个数大小只和\(S[1]\)的大小关系有关

于是每个数的状态设为-1(比S[1]小),1(比S[1]大),0(和S[1]一样)

状态里设的改变是指在这三种状态里的一种变为另一种

如果\(S[u] == S[1]\)或者\(u\)点取max但是\(S[u] < S[1]\)\(u\)点取min但是\(S[u] > S[1]\)

这个时候子树里任意一个点改变\(u\)就会改变,统计两个序列的后缀和并且和当前位置更新即可(因为操作数选最小的那个)

否则的话就是子树里所有与\(S[u] < S[1]\)真假性不同的点都必须改变,剩下的点选了也不用变,统计前缀和并且和当前位置更新(因为操作数选最大的那个)

复杂度\(O(n^2)\)

LOJ提交记录

神仙的100pts正解

我们统计前缀操作,也就是对于一个\(i\)统计\(pre[i]\)是小于等于\(i\)次操作的方案数

我们找出答案都是\(S[1]\)的那条链,容易发现这条链上奇数位置的点挂着的子树,其中的叶子都必须尽量改大(大于\(S[1]\)),而偶数位置的点挂着的子树里面的叶子,都必须尽量改小(小于\(S[1]\)

改成大于\(S[1]\)的最少操作是改成\(S[1] + 1\),小于\(S[1]\)的最少操作是改成\(S[1] - 1\)

以下把\(<S[1 ]\)标成0,$ >S[1]$标成1

于是设\(dp[i][0 / 1]\)表示第\(i\)个点选了一个点集后\(i\)的答案是0或者是1

转移的话就是设\(t = dep[i] \& 1\)

$dp[u][t \oplus 1] = \prod dp[v][t \oplus 1] $

\(dp[u][t] = 2^{leaf[u]} - dp[u][t \oplus 1]\)

最后我们要求的是链上的点不变的方案数,再用总的方案数减去

我们可以只记一个,而我们要用的是\(dp[u][t]\)于是我们只记\(dp[u][t]\)

从小到大枚举最小的操作数,每次可以至多有两个点变为可以更改

发现这个式子可以动态dp转移,于是我们设常数项是\(2^{leaf[u]}\),系数是\(-\prod dp[v][t \oplus 1]\)其中v是\(u\)所有的轻儿子

而叶子节点的值,在变更的时候看一下是否初始值是\(S[u] < S[1]\)而且是需要尽量大的子树(\(S[u] > S[1]\)而且需要尽量小的子树),这个时候可以把叶子节点的常数项改成1,否则叶子节点的值就不需要改变了

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('\n')
#define eps 1e-10
#define MAXN 200005
#define ba 47
//#define ivorysi
using namespace std;
typedef long long int64;
typedef unsigned int u32;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;T f = 1;char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 +c - '0';
        c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
        out(x / 10);
    }
    putchar('0' + x % 10);
}
const int MOD = 998244353;
int inc(int a,int b) {
    return a + b >= MOD ? a + b - MOD : a + b;
}
int mul(int a,int b) {
    return 1LL * a * b % MOD;
}
void update(int &a,int b) {
    a = inc(a,b);
}
int fpow(int x,int c) {
    int res = 1,t = x;
    while(c) {
        if(c & 1) res = mul(res,t);
        t = mul(t,t);
        c >>= 1;
    }
    return res;
}


struct Edge {
    int to,next;
}E[MAXN * 2];
struct val {
    int v,zero;
    val(int a = 0) {
        v = max(a,1);zero = !a;
    }
    friend val operator * (const val &a,const val &b) {
        val c;
        c.v = mul(a.v,b.v);
        c.zero = a.zero + b.zero;
        return c;
    }
    friend val operator / (const val &a,const val &b) {
        val c;
        c.v = mul(a.v,fpow(b.v,MOD - 2));
        c.zero = a.zero - b.zero;
        return c;
    }
    int getval() {
        if(zero) return 0;
        else return v;
    }
}ms[MAXN],all;
struct node {
    int l,r;pair<int,int> rec;
}tr[MAXN * 4];
int head[MAXN],sumE,N,L,R;
int dfn[MAXN],siz[MAXN],son[MAXN],dep[MAXN],idx,lef[MAXN],fa[MAXN];
int top[MAXN],bot[MAXN],line[MAXN];
int S[MAXN],ans[MAXN];
int dp[MAXN][2];
bool nd[MAXN];
void add(int u,int v) {
    E[++sumE].to = v;
    E[sumE].next = head[u];
    head[u] = sumE;
}
void dfs(int u) {
    dep[u] = dep[fa[u]] + 1;
    if(dep[u] % 2 == 0) S[u] = N;
    siz[u] = 1;
    for(int i = head[u] ; i ; i = E[i].next) {
        int v = E[i].to;
        if(v != fa[u]) {
            fa[v] = u;
            dfs(v);
            siz[u] += siz[v];
            if(dep[u] & 1) S[u] = max(S[u],S[v]);
            else S[u] = min(S[u],S[v]);
            if(siz[v] > siz[son[u]]) son[u] = v;
        }
    }
    if(!son[u]) {
        S[u] = u;return;
    }
}
void dfs2(int u,int p = 0) {
    if(S[u] != S[1]) {
        dfn[u] = ++idx;
        if(!top[u]) top[u] = u;
        line[idx] = u;
    }
    for(int i = head[u] ; i ; i = E[i].next) {
        int v = E[i].to;
        if(v != fa[u]) {
            if(S[u] == S[1] && S[1] == S[v]) son[u] = v;
        }
    }
    if(!son[u]) {
        lef[u]++;
        bot[u] = u;
        nd[u] = p;
        if(S[u] == S[1]) {dp[u][0] = 1;ms[u] = 1;}
        else {
            if(S[u] > S[1]) dp[u][1] = 2;
            else dp[u][0] = 2;
        }
        return;
    }
    top[son[u]] = top[u];
    dfs2(son[u],p);
    bot[u] = bot[son[u]];
    lef[u] += lef[son[u]];
    if(S[u] == S[1]) p = dep[u] & 1;
    ms[u] = 1;int t = dep[u] & 1;
    for(int i = head[u] ; i ; i = E[i].next) {
        int v = E[i].to;
        if(v != fa[u] && v != son[u]) {
            dfs2(v,p);
            lef[u] += lef[v];
            ms[u] = ms[u] * dp[v][t ^ 1];
        }
    }
    dp[u][t ^ 1] = mul(ms[u].getval(),dp[son[u]][t ^ 1]);
    dp[u][t] = inc(fpow(2,lef[u]),MOD - dp[u][t ^ 1]);
    if(S[u] == S[1]) all = all * ms[u];
}
pii Merge(pii a,pii b) {
    pii c;
    c.fi = mul(a.fi,b.fi);
    c.se = inc(mul(a.fi,b.se),a.se);
    return c;
}
void updateTr(int u) {
    tr[u].rec = Merge(tr[u << 1].rec,tr[u << 1 | 1].rec);
}
void build(int u,int l,int r) {
    tr[u].l = l;tr[u].r = r;
    if(l == r) {
        int t = line[l];
        tr[u].rec.fi = (MOD - ms[t].getval()) % MOD;tr[u].rec.se = fpow(2,lef[t]);
        if(!son[t]) tr[u].rec.se = dp[t][dep[t] & 1];
        return;
    }
    int mid = (l + r) >> 1;
    build(u << 1,l,mid);
    build(u << 1 | 1,mid + 1,r);
    updateTr(u);
}
void Change(int u,int pos) {
    if(tr[u].l == tr[u].r) {
        int t = line[pos];
        if(!son[t]) {
            if((S[t] > S[1]) != nd[t]) tr[u].rec.se = 1;
        }
        else tr[u].rec.fi = (MOD - ms[t].getval()) % MOD;
        return;
    }
    int mid = (tr[u].l + tr[u].r) >> 1;
    if(pos <= mid) Change(u << 1,pos);
    else Change(u << 1 | 1,pos);
    updateTr(u);
}
pii Query(int u,int l,int r) {
    if(tr[u].l == l && tr[u].r == r) return tr[u].rec;
    int mid = (tr[u].l + tr[u].r) >> 1;
    if(r <= mid) return Query(u << 1,l,r);
    else if(l > mid) return Query(u << 1 |1,l,r);
    else return Merge(Query(u << 1,l,mid),Query(u << 1 | 1,mid + 1,r));
}
void Process(int u) {
    while(S[u] != S[1]) {
        int ori = Query(1,dfn[top[u]],dfn[bot[u]]).se;
        Change(1,dfn[u]);
        int f = fa[top[u]],nw = Query(1,dfn[top[u]],dfn[bot[u]]).se;
        if(S[f] == S[1]) all = all / ms[f];
        ms[f] = ms[f] / ori;
        ms[f] = ms[f] * nw;
        if(S[f] == S[1]) all = all * ms[f];
        u = f;
    }
}
void Init() {
    read(N);read(L);read(R);
    int a,b;
    for(int i = 1 ; i < N ; ++i) {
        read(a);read(b);
        add(a,b);add(b,a);
    }
}
void Solve() {
    all = 1;
    dfs(1);
    dfs2(1);
    ans[1] = fpow(2,lef[1] - 1);
    build(1,1,idx);
    int pw = fpow(2,lef[1]);
    ans[N] = pw - 1;
    for(int i = 2 ; i < N ; ++i) {
        if(S[1] + 1 - i > 1 && !son[S[1] + 1 - i]) {Process(S[1] + 1 - i);}
        if(S[1] - 1 + i <= N && !son[S[1] - 1 + i]) {Process(S[1] - 1 + i);}
        ans[i] = inc(pw,MOD - all.getval());
    }
    for(int i = N ; i >= 1 ; --i) {
        ans[i] = inc(ans[i],MOD - ans[i - 1]);
    }
    for(int i = L ; i <= R ; ++i) {
        out(ans[i]);space;
    }
    enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Init();
    Solve();
    return 0;
}

转载于:https://www.cnblogs.com/ivorysi/p/10965557.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值