传送门

Solution

叶子节点的变化区间是连续的,可得知非叶子节点的权值变化区间也是连续的

由此可知,\(W\)的变化值的可行域也是连续的,所以只需要看它能否变为\(W+1\)或\(W-1\)

对于答案,可以先求消耗能量不超过\(k\)的集合数\(ans_k\),再进行查分

发现\(ans_1=2^{m-1},ans_n=2^{m}-1\)

首先发现,如果要将\(W\)的值\(+1\),那么只会去动那些\(\leq W\)的的节点,让它们的权值变大

类似的,我们发现处理的两类节点无交,所以可以分别求出可以使得\(W\)加一或者减一的概率

因为通过减少较大权值的点的权值的办法都可以被上面的方法取代

当且仅当包含了\(W\)点,只需要耗费\(1\)的能量

接下来考虑不包含\(W\)点的情况,假如我们要求\(ans_k\)

把\(<W\)的节点称作小节点,\(>W\)的节点称作大结点

设\(f_i\)表示只改动小节点的值但是\(i\)点权值仍然\(\leq W\)的概率

奇数点:

\[f_u=\prod f_v
\]

偶数点:

\[f_u=1-\prod(1-f_v)
\]

设\(g_i\)表示可以通过改变大结点来使得\(i\)点权值\(<W\)的概率,转移与\(f_i\)相似

所以,得到

\[ans_k=2^{m-1}(1-f_1(1-g_1))+2^{m-1}
\]

其中\(f_1(1-g_1)\)表示的是根节点权值保持不变的概率

\(k\)每递增\(1\)最多改变两个叶子点的\(dp\)值

动态\(dp\)实现

对于奇偶深度的叶子问题

可以使得偶层节点\(f_i’=1-f_i,g_i'=1-g_i\)

这样,我们都可以通过这样来转移了

\[f_u=\prod (1-f_v)\\g_u=\prod(1-g_v)
\]

也是毒瘤题。。。

有可能会除以\(0\),所以记录一下乘了\(0\)的个数,和不含\(0\)的其它项的积


突然发现自己忘记ddp的做法,不妨重温一下

首先我们设了函数\(f\)和\(g\),(和上述函数无关)

分别表示算了重儿子和没算重儿子的答案

在本题中,它们的转移如下

如果是叶子节点,或者某个重链的链底

则有\(g_i=f_i\)

然后有(序号表示的是距离链头的距离\(+1\))

\[f_1=(1-f_2)g_1
\\f_2=(1-f3)g_2
\\...\\f_n=g_n\\
f_1=g_1-g_1g_2+g_1g_3g_4-...+(-1)^{n+1}(g_1g_2...g_n)
\]

由此可以发现,在维护那个线段树区间乘法的同时,还需要维护一个如上的\(Sum\)值

总结:

之前做ddp的时候,是使用线段树维护一个链的转移矩阵的积,并不能直接求出链顶的答案

而在此题,我们直接将修改链底的答案,并更新进线段树,通过计算直接得到链顶的答案,线段树可以直接取代\(f\)数组

另外不需要建立庞大的所有点的线段树

可以分别对每条链建一棵,这样就只需要一个\(Modify\),而不用\(Query\)

最后,这真是一道毒瘤题

Code 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define reg register
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
const int P=998244353,MN=2e5+5;
int Mul(int x,int y){return (1ll*x*y)%P;}
int Add(int x,int y){return (x+y)%P;}
int fpow(int x,int m){int r=1;for(;m;m>>=1,x=Mul(x,x))if(m&1)r=Mul(r,x);return r;}
struct node
{
int a,cnt;
node(){a=1,cnt=0;}
node(int a,int cnt):a(a),cnt(cnt){}
void mul(node o){a=Mul(a,o.a),cnt+=o.cnt;}
void div(node o){a=Mul(a,fpow(o.a,P-2)),cnt-=o.cnt;}
int val(){return cnt?0:a;}
}f[MN],g[MN];
node _(int x){x%=P;return x?node(x,0):node(1,1);}
struct edge{int to,nex;}e[MN<<1];
bool lf[MN];
int hr[MN],en,mx[MN],siz[MN],top[MN],bot[MN],fa[MN],dfn[MN],fdfn[MN],dep[MN],dind=0,leaf=1;
int n,W,ans[MN],F[MN],G[MN];
inline void ins(int x,int y)
{
e[++en]=(edge){y,hr[x]};hr[x]=en;
e[++en]=(edge){x,hr[y]};hr[y]=en;
}
void rw(int &x,int y,int z){z?x=max(x,y):x=min(x,y);}
int dfs1(int x,int f,int d)
{
register int i;fa[x]=f;siz[x]=1;dep[x]=d;
int res=d&1?0:0x3f3f3f3f;
for(i=hr[x];i;i=e[i].nex)if(e[i].to^f)
{
rw(res,dfs1(e[i].to,x,d+1),d&1);
siz[x]+=siz[e[i].to],siz[e[i].to]>siz[mx[x]]?mx[x]=e[i].to:0;
}
if(siz[x]==1){lf[x]=1;leaf=(leaf<<1)%P;return x;}
return res;
}
int rt[MN],ls[MN<<2],rs[MN<<2],sum_f[MN<<2],sum_g[MN<<2],prd_f[MN<<2],prd_g[MN<<2],tt;
void Build(int &x,int l,int r);
void dfs2(int x,int tp)
{
fdfn[dfn[x]=++dind]=x;top[x]=tp;
register int i;if(mx[x])dfs2(mx[x],tp);
for(i=hr[x];i;i=e[i].nex)if((e[i].to^fa[x])&&(e[i].to^mx[x]))dfs2(e[i].to,e[i].to);
if(mx[x])
{
F[x]=G[x]=1;
for(i=hr[x];i;i=e[i].nex)if(e[i].to^fa[x])
{
F[x]=Mul(F[x],(1+P-F[e[i].to]));
G[x]=Mul(G[x],(1+P-G[e[i].to]));
if(e[i].to^mx[x])
f[x].mul(_(1+P-F[e[i].to])),g[x].mul(_(1+P-G[e[i].to]));
}
}
else
{
F[x]=(x>W)^(dep[x]&1),G[x]=(x>=W)^(dep[x]&1);
bot[top[x]]=x;
f[x]=_((x>W)^(dep[x]&1));g[x]=_((x>=W)^(dep[x]&1));
}
if(x==tp) Build(rt[x],dfn[x],dfn[bot[x]]);
}
void up(int x,int l)
{
sum_f[x]=Add(sum_f[ls[x]],Mul((l&1?P-prd_f[ls[x]]:prd_f[ls[x]]),sum_f[rs[x]]));
prd_f[x]=Mul(prd_f[ls[x]],prd_f[rs[x]]);
sum_g[x]=Add(sum_g[ls[x]],Mul((l&1?P-prd_g[ls[x]]:prd_g[ls[x]]),sum_g[rs[x]]));
prd_g[x]=Mul(prd_g[ls[x]],prd_g[rs[x]]);
}
void update(int x,int a){sum_f[x]=prd_f[x]=f[a].val();sum_g[x]=prd_g[x]=g[a].val();}
void Build(int &x,int l,int r)
{
x=++tt;
if(l==r)return(void)(update(x,fdfn[l]));
int mid=(l+r)>>1;
Build(ls[x],l,mid);Build(rs[x],mid+1,r);
up(x,mid-l+1);
}
void Modify(int x,int l,int r,int a)
{
if(l==r)return(void)(update(x,fdfn[l]));
int mid=(l+r)>>1;
a<=mid?Modify(ls[x],l,mid,a):Modify(rs[x],mid+1,r,a);
up(x,mid-l+1);
}
#define o top[x]
void solve_f(int x)
{
if(fa[o]) f[fa[o]].div(_(P+1-sum_f[rt[o]]));
Modify(rt[o],dfn[o],dfn[bot[o]],dfn[x]);
if(fa[o]) f[fa[o]].mul(_(P+1-sum_f[rt[o]])),solve_f(fa[o]);
}
void solve_g(int x)
{
if(fa[o]) g[fa[o]].div(_(P+1-sum_g[rt[o]]));
Modify(rt[o],dfn[o],dfn[bot[o]],dfn[x]);
if(fa[o]) g[fa[o]].mul(_(P+1-sum_g[rt[o]])),solve_g(fa[o]);
}
int main()
{
int L,R;
n=read();L=read();R=read();
register int i,j;
for(i=1;i<n;++i) j=read(),ins(j,read());
W=dfs1(1,0,1);dfs2(1,1);
ans[1]=leaf=Mul(leaf,(P+1)>>1);
for(i=2;i<n;++i)
{
if(W+1-i>=1&&lf[W+1-i]) f[W+1-i]=_(P+1>>1),solve_f(W+1-i);
if(W+i-1<=n&&lf[W+i-1]) g[W+i-1]=_(P+1>>1),solve_g(W+i-1);
ans[i]=Mul(Add(Mul(P-sum_f[rt[1]],P+1-sum_g[rt[1]]),2),leaf);
}
ans[n]=Add(Mul(leaf,2),P-1);
for(i=L;i<=R;++i)
printf("%d ",Add(ans[i],P-ans[i-1]));
return 0;
}

Blog来自PaperCloud,未经允许,请勿转载,TKS!

「ZJOI2019」Minmax搜索的更多相关文章

  1. LOJ3044. 「ZJOI2019」Minimax 搜索

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

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

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

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

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

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

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

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

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

  6. Loj #3045. 「ZJOI2019」开关

    Loj #3045. 「ZJOI2019」开关 题目描述 九条可怜是一个贪玩的女孩子. 这天,她和她的好朋友法海哥哥去玩密室逃脱.在他们面前的是 \(n\) 个开关,开始每个开关都是关闭的状态.要通过 ...

  7. Loj #3042. 「ZJOI2019」麻将

    Loj #3042. 「ZJOI2019」麻将 题目描述 九条可怜是一个热爱打麻将的女孩子.因此她出了一道和麻将相关的题目,希望这题不会让你对麻将的热爱消失殆尽. 今天,可怜想要打麻将,但是她的朋友们 ...

  8. 【LOJ】#3046. 「ZJOI2019」语言

    LOJ#3046. 「ZJOI2019」语言 先orz zsy吧 有一个\(n\log^3n\)的做法是把树链剖分后,形成logn个区间,这些区间两两搭配可以获得一个矩形,求矩形面积并 然后就是对于一 ...

  9. 【LOJ】#3042. 「ZJOI2019」麻将

    LOJ#3042. 「ZJOI2019」麻将 如何判定一个集合牌有没有胡的子集是不是胡的 就用一个\(dp[j][k][0/1]\)表示有j个连续两个的串,有k个连续1个串,有没有对子,再记一下这个集 ...

随机推荐

  1. Git下载安装及设置详细教程

    Git下载安装及设置详细教程 一.安装前准备   1. 廖雪峰老师Git教程 :推荐Git入门教程.  2. 按照自己的系统版本下载Git软件,我的操作系统:Windows7 64位,安装版本为Git ...

  2. Flask 进阶

    OOP 面向对象反射 # __call__方法 # class Foo(object): # def __call__(self, *args, **kwargs): # return "i ...

  3. java开发手册-总结与补充

    1.分层领域模型规约 1.DO( Data Object):与数据库表结构一一对应,通过 DAO 层向上传输数据源对象. 2.DTO( Data Transfer Object):数据传输对象, Se ...

  4. js switch case 判断的是绝对相对===,值和类型都要相等

    js switch case 判断的是绝对相对===,值和类型都要相等

  5. 查看Windows系统进程(PID)

    语法:tasklist /svc 作用:打印系统进程,并显示其对应PID,可用来跟踪进程并根据PID来进行关闭.

  6. H3C 帧聚合

  7. 三、Linux_环境变量

    环境变量配置: # 每次进入命令都要重新source /etc/profile 才能生效? # 解决办法:将环境变量放置到~/.bashrc文件中 $ vim ~/.bashrc # 在里面添加相关的 ...

  8. CentOS 7网络配置工具

    CentOS 7网络配置工具 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.网卡命名机制 CentOS 6之前,网络接口使用连续号码命名:eth0.eth1等,当增加或删除网卡 ...

  9. OpenStack共享组件-Memcache缓存系统

    1. 缓存系统 1.1 静态web页面 1> 在静态Web程序中,客户端使用Web浏览器(IE.FireFox等)经过网络(Network)连接到服务器上,使用HTTP协议发起一个请求(Requ ...

  10. Win10 微软远程桌面很模糊是为什么?

    今天又查了一下,解决了问题,是 Intel 集显驱动引起的.在桌面右键 => 图形属性 => 在蓝色的 Intel 核芯显卡控制面板上选择“ 3D ” => 在“保守形态学抗锯齿”中 ...