先求出根节点的权值\(w\)。根据套路,我们对于每个\(k\),计算\(w(s)\leq k\)的方案数,差分得到答案。为了方便,接下来考虑计算概率而不是方案数。

可以发现,对于一个给定的有解的子集,在最优解下,根节点的权值一定可以是\(w+1\)或\(w-1\)。如果我们希望把根节点的权值变为\(w+1\),那么我们一定只会改变点权\(\leq w\)的点,\(w-1\)同理。

我们特殊考虑点权为\(w\)的点。如果控制集合包含这个点,那么答案一定为\(1\)(因为所有点的点权两两不同),所以\(ans_1=\frac{1}{2}\)。接下来,我们可以钦定控制集合不包含这个点。

这时,如果我们希望把根节点的权值变为\(w+1\),我们一定只会改变点权\(<w\)的点,\(w-1\)同理。这样这两类点就无交了。所以我们可以算出 通过改变点权\(<w\)的点不能使根节点的权值变为\(w+1\)的概率\(f\), 和 通过改变点权\(>w\)的点不能使根节点的权值变为\(w-1\)的概率\(g\),乘起来就是答案。

转移显然,分奇偶层讨论一下就行了。为了方便,我们将偶数层的\(f,g\)值取反,这样对于每一层转移都是一样的,就可以直接上动态DP了。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int mod=998244353;
const int inv2=(mod+1)>>1; int gi() {
int x=0,o=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') o=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*o;
} int qpow(int a,int b) {
int ret=1;
while(b) {
if(b&1) ret=1ll*ret*a%mod;
a=1ll*a*a%mod,b>>=1;
}
return ret;
} struct num {
int x,y;
num(int x=1,int y=0):x(x),y(y) {}
void mul(const num &A) {
x=1ll*x*A.x%mod,y+=A.y;
}
void div(const num &A) {
x=1ll*x*qpow(A.x,mod-2)%mod,y-=A.y;
}
int val() {
return y?0:x;
}
} g[N][2]; int add(int a,int b) {
return a+b>=mod?a+b-mod:a+b;
} int sub(int a,int b) {
return a-b<0?a-b+mod:a-b;
} num trs(int x) {
return x?num(x,0):num(1,1);
} vector<int> E[N];
int n,L,R,pw=1,fa[N],dep[N],siz[N],son[N],f[N][2],top[N],tim=0,dfn[N],bot[N],id[N],w,t,rt[N],tot=0,ls[N*50],rs[N*50],s[N*50][2],p[N*50][2],ans[N];
bool lf[N]; int dfs1(int u) {
siz[u]=1;
if(fa[u]&&E[u].size()==1) { lf[u]=1;pw=(pw<<1)%mod;return u; }
int w=dep[u]?n:0;
for(auto v:E[u]) if(v!=fa[u]) fa[v]=u,dep[v]=dep[u]^1,t=dfs1(v),siz[u]+=siz[v],siz[son[u]]<siz[v]?son[u]=v:0,w=dep[u]?min(w,t):max(w,t);
return w;
} void dfs2(int u) {
bot[top[u]]=u;id[dfn[u]=++tim]=u;f[u][0]=f[u][1]=1;
if(son[u]) {
top[son[u]]=top[u],dfs2(son[u]);
for(int i=0;i<2;i++) f[u][i]=1ll*f[u][i]*sub(1,f[son[u]][i])%mod;
}
else f[u][0]=(t=((u<=w)^dep[u])),g[u][0].mul(trs(t)),f[u][1]=(t=((u<w)^dep[u])),g[u][1].mul(trs(t));
for(auto v:E[u])
if(v!=fa[u]&&v!=son[u]) {
top[v]=v,dfs2(v);
for(int i=0;i<2;i++) g[u][i].mul(trs(t=sub(1,f[v][i]))),f[u][i]=1ll*f[u][i]*t%mod;
}
} #define chk if(l==r) { for(int i=0;i<2;i++) s[x][i]=p[x][i]=g[id[l]][i].val(); return; } #define up for(int i=0;i<2;i++) s[x][i]=add(s[ls[x]][i],1ll*(((mid-l+1)&1)?mod-p[ls[x]][i]:p[ls[x]][i])*s[rs[x]][i]%mod),p[x][i]=1ll*p[ls[x]][i]*p[rs[x]][i]%mod; void build(int &x,int l,int r) {
x=++tot;chk;
int mid=(l+r)>>1;
build(ls[x],l,mid),build(rs[x],mid+1,r);
up;
} void mdf(int x,int l,int r,int k) {
chk;
int mid=(l+r)>>1;
k<=mid?mdf(ls[x],l,mid,k):mdf(rs[x],mid+1,r,k);
up;
} void mdf(int u,int x) {
int v=top[u],w=fa[v];
if(w) g[w][x].div(trs(sub(1,s[rt[v]][x])));
mdf(rt[v],dfn[v],dfn[bot[v]],dfn[u]);
if(w) g[w][x].mul(trs(sub(1,s[rt[v]][x]))),mdf(w,x);
} int main() {
n=gi(),L=gi(),R=gi();
for(int i=1,u,v;i<n;i++) u=gi(),v=gi(),E[u].push_back(v),E[v].push_back(u);
w=dfs1(1);top[1]=1;dfs2(1);
for(int i=1;i<=n;i++) if(top[i]==i) build(rt[i],dfn[i],dfn[bot[i]]);
ans[1]=pw=1ll*pw*inv2%mod;
for(int i=2;i<n;i++) {
if((t=w+1-i)>0&&lf[t]) g[t][0]=trs(inv2),mdf(t,0);
if((t=w+i-1)<=n&&lf[t]) g[t][1]=trs(inv2),mdf(t,1);
ans[i]=1ll*add(1ll*sub(s[1][1],1)*s[1][0]%mod,2)*pw%mod;
}
ans[n]=(pw+pw-1)%mod;
for(int i=L;i<=R;i++) printf("%d ",sub(ans[i],ans[i-1]));
return 0;
}

[ZJOI2019]Minimax搜索的更多相关文章

  1. [ZJOI2019]Minimax搜索(线段树+动态DP+树剖)

    为什么我怎么看都只会10pts?再看还是只会50~70?只会O(n2(R-L+1))/O(nlogn(R-L+1))……一眼看动态DP可还是不会做…… 根节点的答案是叶子传上来的,所以对于L=R的数据 ...

  2. LOJ3044. 「ZJOI2019」Minimax 搜索

    LOJ3044. 「ZJOI2019」Minimax 搜索 https://loj.ac/problem/3044 分析: 假设\(w(1)=W\),那么使得这个值变化只会有两三种可能,比\(W\)小 ...

  3. Loj #3044. 「ZJOI2019」Minimax 搜索

    Loj #3044. 「ZJOI2019」Minimax 搜索 题目描述 九条可怜是一个喜欢玩游戏的女孩子.为了增强自己的游戏水平,她想要用理论的武器武装自己.这道题和著名的 Minimax 搜索有关 ...

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

    LOJ#3044. 「ZJOI2019」Minimax 搜索 一个菜鸡的50pts暴力 设\(dp[u][j]\)表示\(u\)用\(j\)次操作能使得\(u\)的大小改变的方案数 设每个点的初始答案 ...

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

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ468.html 前言 毒瘤题 题解 首先,将问题稍加转化,将"等于k"转化为"小于等于k&q ...

  6. [LOJ#3044][动态DP]「ZJOI2019」Minimax 搜索

    题目传送门 容易想到一种暴力 DP:先转化成对于每个 \(k\) 求出 \(\max_{i\in S}|i-w_i|\le k\) 的方案数,最后差分 然后问题转化成每个叶子的权值有个取值区间,注意这 ...

  7. 「ZJOI2019」&「十二省联考 2019」题解索引

    「ZJOI2019」&「十二省联考 2019」题解索引 「ZJOI2019」 「ZJOI2019」线段树 「ZJOI2019」Minimax 搜索 「十二省联考 2019」 「十二省联考 20 ...

  8. ZJOI2019做题笔记

    麻将(期望.DP套DP) 先考虑如何计算一个子集是否能胡. 设\(f_{i,0/1,j,k}\)表示考虑了子集中\(1 \sim i\)的牌,是否找到对子,\(i-1,i,i+1\)预计拿\(j\)个 ...

  9. ZJOI2019 Day1 题解

    想要继续向前,就从克服内心的恐惧开始. 麻将 题意 在麻将中,我们称点数连续的三张牌或三张点数一样的成为面子,称两张点数一样的牌为对子.一副十四张麻将牌的胡牌条件是可以分成四个面子和一个对子或者分成七 ...

随机推荐

  1. 在 macOS 中激活 Astash Professional

    Astah Professional v7.2.0-1ff236版安装完毕后,直接用压缩包内的 astah-pro.jar 替换原安装目录内的同名文件(/Applications/astah prof ...

  2. Linux之chmod使用

    Linux文件分三种身份和四中权限. u:文件的拥有者 g:文件所属的群组 o:其他用户 对于每个身份,又有四种权限,分别为: r:读取文件的权限(read) w:写入文件的权限(write) x:执 ...

  3. PHP语言性能优化——少使用魔术方法

    对以下使用魔术方法和不适用魔术方法运行时间进行比较 使用魔术方法test1.php: <?php /** * 测试类 */ class test { private $name = " ...

  4. Android 实现在Activity中操作刷新另外一个Activity数据列表

    做android项目中遇到这样一个问题:有两个acticity,一个显示好友列表,另外一个显示会话列表,现在问题是在会话界面增加一个添加好友功能,添加好友后要求实时的刷新好友列表. 想了想,找了两种方 ...

  5. 在Eclipse中使用Struts和Hibernate框架搭建Maven Web项目

    前言 学习使用Java还是2012年的事情,刚开始学习的Java的时候,使用的是MyEclipse工具和SSH框架.初学者适合使用MyEclipse,因为他将struts.Spring和Hiberna ...

  6. m2e 插件

    官网 http://www.eclipse.org/m2e/ 在线安装地址 http://download.eclipse.org/technology/m2e/releases 插件简介 Launc ...

  7. 安装gcc及其依赖

    在gcc-4.8.2和gcc-4.1.2基础上编译gcc-5.2.0,有可能会遇到一些问题. 要想成功编译gcc,则在编译之前需要安装好它的至少以下三个依赖: gmp mpfr mpc 而mpc又依赖 ...

  8. Linux 基础教程 28-nc命令

    nc     nc命名netcat,直译为网络猫.在CentOS 7查看帮助的解释如下所示: ncat - Concatenate and redirect sockets 翻译过来就是可以连接和重定 ...

  9. vc++ 不同对话框中传递信息的方法(基于自定义消息SendMessage) (转载)

    转载自:http://blog.csdn.net/myj0513/article/details/6827360 背景: 新建了一个基于对话框的MFC程序,在主对话框中添加tabcontrol控件,又 ...

  10. Oracle EBS Patch Demo

    Oracle EBS APP & DB 打补丁过程简述l例子: 打 Patch#   11843100:R12.CAC.B 打PATCH之前先查询一下是否已经有了这个PATCH. SELECT ...