UOJ#468. 【ZJOI2019】Minimax搜索 动态DP

原文链接www.cnblogs.com/zhouzhendong/p/UOJ468.html

前言

毒瘤题

题解

首先,将问题稍加转化,将“等于k”转化为“小于等于k”减去“小于k”。

然后,考虑在有一个变化量限制k时,所有的叶子会怎样变化。

我们称原本根的权值对应的节点到根的路径为“主链”,那么,只要主链的任何一个节点的权值发生变化,那么根节点权值就发生变化。

我们称一个主链上的节点的在主链上的儿子为“主儿子”。

对于主链上的一个节点,假设他深度为奇数,那么他的所有子树中,除了主儿子所在子树以外的所有叶子节点都会加上k。否则,假设他深度为偶数,那么这些叶子节点会减去k。

于是,接下来我们就可以得到一个 \(O(n)\) 的 DP 方法。即

对于深度为奇数的主链的子树, \(dp[x][0]\)、\(dp[x][1]\) 分别表示在子树x的所有叶子节点集合操作时,有 \(dp[x][1]\) 个集合可以使得 x 的权值大于 1 号点原先的权值,有 \(dp[x][0]\) 个不能。

对于深度为偶数的主链的子树, \(dp[x][1]\)、\(dp[x][0]\) 分别表示在子树x的所有叶子节点集合操作时,有 \(dp[x][1]\) 个集合可以使得 x 的权值小于 1 号点原先的权值,有 \(dp[x][1]\) 个不能。

请读者自行列出 DP 转移。

事实上,这种 DP 状态是可以简化的。由于对于一个 x,\(dp[x][0]+dp[x][1]\) 是固定的,所以我们只需要记一个。虽然这不影响得分。

至此,我们得到了一个单次 DP \(O(n)\),总时间复杂度 \(O((R-L)\cdot n)\) 的做法。

我们发现,当k的值从小到大不断变大时,只会有 \(O(n)\) 个叶子的 DP 值发生变化,每次变化会影响它到根的路径。

简单分析即可发现这里可以用动态DP来维护。

于是我们就得到了一个 \(O(n\log ^ 2 n)\) 的做法。

注意维护的时候可以会遇到乘0和除0的情况,注意特判。

代码

#pragma GCC optimize("Ofast","inline")
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof x)
#define For(i,a,b) for (int i=(a);i<=(b);i++)
#define Fod(i,b,a) for (int i=(b);i>=(a);i--)
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define fi first
#define se second
#define next Next
#define outval(x) cerr<<#x" = "<<x<<endl
#define outtag(x) cerr<<"-----------------"#x"-----------------\n"
#define outarr(a,L,R) cerr<<#a"["<<L<<".."<<R<<"] = ";\
                    For(_x,L,R) cerr<<a[_x]<<" ";cerr<<endl;
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair <int,int> pii;
LL read(){
    LL x=0,f=0;
    char ch=getchar();
    while (!isdigit(ch))
        f=ch=='-',ch=getchar();
    while (isdigit(ch))
        x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}
const int N=200005,mod=998244353;
int Pow(int x,int y){
    int ans=1;
    for (;y;y>>=1,x=(LL)x*x%mod)
        if (y&1)
            ans=(LL)ans*x%mod;
    return ans;
}
void Add(int &x,int y){
    if ((x+=y)>=mod)
        x-=mod;
}
void Del(int &x,int y){
    if ((x-=y)<0)
        x+=mod;
}
int Add(int x){
    return x>=mod?x-mod:x;
}
int Del(int x){
    return x<0?x+mod:x;
}
int n,qL,qR;
vector <int> e[N];
int depth[N],size[N],son[N],val[N];
int fa[N],leaf[N],clf[N],pw2[N],clf2[N];
int fun(int k,int a,int b){
    return k&1?max(a,b):min(a,b);
}
void dfs(int x,int pre,int d){
    depth[x]=d,size[x]=1,son[x]=0,fa[x]=pre;
    if (e[x].size()==1&&d>1)
        val[x]=x,leaf[x]=clf[x]=1;
    else
        val[x]=d&1?0:n;
    for (auto y : e[x])
        if (y!=pre){
            dfs(y,x,d+1);
            clf[x]+=clf[y];
            size[x]+=size[y];
            val[x]=fun(d,val[x],val[y]);
            if (!son[x]||size[y]>size[son[x]])
                son[x]=y;
        }
}
int next[N],fad[N];
void dfs2(int x,int d){
    fad[x]=d;
    for (auto y : e[x])
        if (y!=fa[x]&&y!=next[x])
            dfs2(y,d);
}
void GetNext(){
    int x=val[1];
    for (int y=fa[x];y;x=y,y=fa[x])
        next[y]=x,dfs2(y,depth[y]);
}
int top[N],I[N],aI[N],Time=0;
void GetTop(int x,int tp){
    top[x]=tp,I[x]=++Time,aI[Time]=x;
    if (son[x]&&son[x]!=next[x])
        GetTop(son[x],tp);
    for (auto y : e[x])
        if (!I[y])
            GetTop(y,y);
}
int ans[N];
int dp[N];
int delta;
void DP(int x){
    dp[x]=0;
    if (x==val[1]){
        dp[x]=1;
        return;
    }
    if (leaf[x]){
        if (fad[x]&1){
            dp[x]+=(x+delta>val[1])==(depth[x]&1);
            dp[x]+=(x>val[1])==(depth[x]&1);
        }
        else {
            dp[x]+=(x-delta>=val[1])==(depth[x]&1);
            dp[x]+=(x>=val[1])==(depth[x]&1);
        }
        return;
    }
    dp[x]=1;
    for (auto y : e[x])
        if (y!=fa[x]){
            DP(y);
            if (y!=next[x])
                dp[x]=(LL)dp[x]*dp[y]%mod;
        }
    dp[x]=Del(pw2[clf2[x]]-dp[x]);
}
struct int0{
    int v,c;
    int0(){}
    int0(int _v,int _c){
        v=_v,c=_c;
    }
    int f(){
        return c?0:v;
    }
    void operator *= (int x){
        if (!x)
            c++;
        else
            v=(LL)v*x%mod;
    }
    void operator /= (int x){
        if (!x)
            c--;
        else
            v=(LL)v*Pow(x,mod-2)%mod;
    }
}v2[N],now;
int0 Get(){
    int0 ans=int0(1,0);
    for (int x=val[1];x;x=fa[x])
        ans*=Del(pw2[clf2[x]]-dp[x]);
    return ans;
}
struct fuck{
    int a,b;
    fuck(){}
    fuck(int _a,int _b){
        a=_a,b=_b;
    }
    friend fuck operator * (fuck x,fuck y){
        return fuck((LL)x.a*y.a%mod,((LL)x.a*y.b+x.b)%mod);
    }
    int calc(int x){
        return ((LL)a*x+b)%mod;
    }
}v[N];
int mxd[N];
namespace Seg{
    fuck s[N<<2];
    void Build(int rt,int L,int R){
        if (L==R)
            return (void)(s[rt]=v[aI[L]]);
        int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
        Build(ls,L,mid);
        Build(rs,mid+1,R);
        s[rt]=s[ls]*s[rs];
    }
    void update(int rt,int L,int R,int x,fuck v){
        if (L==R)
            return (void)(s[rt]=v);
        int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
        if (x<=mid)
            update(ls,L,mid,x,v);
        else
            update(rs,mid+1,R,x,v);
        s[rt]=s[ls]*s[rs];
    }
    fuck query(int rt,int L,int R,int xL,int xR){
        if (R<xL||L>xR)
            return fuck(1,0);
        if (xL<=L&&R<=xR)
            return s[rt];
        int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
        return query(ls,L,mid,xL,xR)*query(rs,mid+1,R,xL,xR);
    }
}
void update(int x){
    while (1){
        Seg::update(1,1,n,I[x],v[x]);
        x=top[x];
        int f=fa[x];
        if (val[x]!=val[1])
            v2[f]/=dp[x];
        else
            now/=Del(pw2[clf2[x]]-dp[x]);
        dp[x]=Seg::query(1,1,n,I[x],I[mxd[x]]).calc(1);
        if (val[x]!=val[1])
            v2[f]*=dp[x],v[f].a=v2[f].f(),x=f;
        else {
            now*=Del(pw2[clf2[x]]-dp[x]);
            break;
        }
    }
}
vector <int> upds[N];
int main(){
    n=read(),qL=read(),qR=read();
    pw2[0]=1;
    For(i,1,n)
        pw2[i]=Add(pw2[i-1]<<1);
    For(i,1,n-1){
        int x=read(),y=read();
        e[x].pb(y),e[y].pb(x);
    }
    dfs(1,0,1);
    GetNext();
    GetTop(1,1);
    For(i,1,n)
        clf2[i]=clf[i];
    for (int x=val[1];x;x=fa[x])
        clf2[x]-=clf[next[x]];
    delta=1,DP(1),now=Get(),ans[1]=Del(pw2[clf[1]]-now.f());
    For(x,1,n){
        if (leaf[x])
            v[x]=fuck(0,dp[x]);
        else {
            v2[x]=int0(Del(-1),0);
            for (auto y : e[x])
                if (y!=fa[x]&&y!=next[x]&&y!=son[x])
                    v2[x]*=dp[y];
            v[x]=fuck(v2[x].f(),pw2[clf2[x]]);
        }
    }
    For(x,1,n)
        if (top[x]==x)
            mxd[x]=x;
    For(x,1,n)
        if (depth[x]>depth[mxd[top[x]]])
            mxd[top[x]]=x;
    Seg::Build(1,1,n);
    For(i,1,n){
        if (!leaf[i]||i==val[1])
            continue;
        if (fad[i]&1){
            if (i<val[1])
                upds[val[1]-i+1].pb(i);
        }
        else {
            if (i>val[1])
                upds[i-val[1]+1].pb(i);
        }
    }
    For(i,2,n-1){
        delta=i;
        for (auto x : upds[i]){
            int tmp=0;
            if (fad[x]&1){
                tmp+=(x+delta>val[1])==(depth[x]&1);
                tmp+=(x>val[1])==(depth[x]&1);
            }
            else {
                tmp+=(x-delta>=val[1])==(depth[x]&1);
                tmp+=(x>=val[1])==(depth[x]&1);
            }
            v[x]=fuck(0,tmp);
            update(x);
        }
        ans[i]=Del(pw2[clf[1]]-now.f());
    }
    ans[n]=Del(pw2[clf[1]]-1);
    For(i,qL,qR)
        printf("%d ",Del(ans[i]-ans[i-1]));
    puts("");
    return 0;
}
上一篇:Tcp流式传输解决粘包和少包的方案


下一篇:田小花语音机器人(五)python使用socke模块建立多客户端链接同一个服务器