[ZJOI2019]Minimax搜索(线段树+动态DP+树剖)
为什么我怎么看都只会10pts?再看还是只会50~70?只会O(n2(R-L+1))/O(nlogn(R-L+1))……一眼看动态DP可还是不会做……
根节点的答案是叶子传上来的,所以对于L=R的数据,可以直接枚举需要±n的叶子节点个数num,然后答案就是2num,每次枚举时重新扫描一下就是O(n2(R-L+1))。然后发现可以动态DP,不需要每次O(n),于是可以优化到O(nlogn(R-L+1))。
然后可以发现若叶子x为答案,叶子x到根的链上的DP值都为x,而一旦更改,要么更改根的值,要么更改链上任意一个点的值,显然更改链上最优。因此只要求出这条链上权值不变的方案数,再用总方案数减去这个方案数就是这条答案链的方案数。所以根据前面说的50/70分思想,每次只需更改一个叶子,考虑动态DP,然后轻重链剖分,每次沿着重链往上跳即可。时间复杂度O((R-L)log2n)
#include<bits/stdc++.h>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int N=2e5+,mod=;
struct mat{int a,b;}a[N],tr[N<<];
mat operator*(mat a,mat b){return(mat){1ll*a.a*b.a%mod,(1ll*a.a*b.b+a.b)%mod};}
int n,L,R,ret=,cnt,pw[N],fa[N],dep[N],val[N],flag[N],q[N][],s[N],f[N];
int son[N],sz[N],pos[N],top[N],dfn[N],ed[N],rt[N],ans[N];
vector<int>G[N];
int qpow(int a,int b)
{
int ret=;
while(b)
{
if(b&)ret=1ll*ret*a%mod;
a=1ll*a*a%mod,b>>=;
}
return ret;
}
void dfs1(int u)
{
dep[u]=dep[fa[u]]+,sz[u]=,val[u]=(dep[u]&)?:n,s[u]=;
bool leaf=;
for(int i=;i<G[u].size();i++)
if(G[u][i]!=fa[u])
{
leaf=,fa[G[u][i]]=u;
dfs1(G[u][i]);
sz[u]+=sz[G[u][i]],s[u]=1ll*s[u]*s[G[u][i]]%mod;
val[u]=(dep[u]&)?max(val[u],val[G[u][i]]):min(val[u],val[G[u][i]]);
if(sz[G[u][i]]>sz[son[u]])son[u]=G[u][i];
}
if(leaf)val[u]=u,s[u]=;else a[u].b=s[u];
}
void dfs2(int u,int tp)
{
top[u]=tp,dfn[u]=++cnt,pos[cnt]=u;
if(son[u])dfs2(son[u],tp);else ed[tp]=cnt;
for(int i=;i<G[u].size();i++)
if(G[u][i]!=son[u]&&G[u][i]!=fa[u])dfs2(G[u][i],G[u][i]);
}
void dp(int u,int rt1)
{
rt[u]=rt1;
bool leaf=;
int ret=mod-;
for(int i=;i<G[u].size();i++)
if(G[u][i]!=fa[u])
{
leaf=,dp(G[u][i],rt1);
if(G[u][i]!=son[u])ret=1ll*ret*f[G[u][i]]%mod;
}
if(!leaf)
{
if(flag[rt1])
{
f[u]=a[u].a=(dep[u]&)?-(u<=val[]):(u<=val[]);
if(u<=val[])q[val[]-u][++q[val[]-u][]]=u;
}
else{
f[u]=a[u].a=(dep[u]&)?(u>=val[]):-(u>=val[]);
if(u>=val[])q[u-val[]][++q[u-val[]][]]=u;
}
return;
}
a[u].a=ret,f[u]=(1ll*ret*f[son[u]]+s[u])%mod;
}
void dfs(int u)
{
for(int i=;i<G[u].size();i++)
if(G[u][i]!=fa[u])
{
if(val[u]==val[G[u][i]])dfs(G[u][i]);
else{
dfs2(G[u][i],G[u][i]);
flag[G[u][i]]=(dep[u]&);
dp(G[u][i],G[u][i]);
ret=1ll*ret*f[G[u][i]]%mod;
}
}
}
void build(int l,int r,int rt)
{
if(l==r){tr[rt]=a[pos[l]];return;}
int mid=l+r>>;
build(lson),build(rson);
tr[rt]=tr[rt<<]*tr[rt<<|];
}
void update(int k,int v,int l,int r,int rt)
{
if(l==r){tr[rt].a=v;return;}
int mid=l+r>>;
if(k<=mid)update(k,v,lson);else update(k,v,rson);
tr[rt]=tr[rt<<]*tr[rt<<|];
}
mat query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)return tr[rt];
int mid=l+r>>;
if(L<=mid&&R>mid)return query(L,R,lson)*query(L,R,rson);
if(L<=mid)return query(L,R,lson);
if(R>mid)return query(L,R,rson);
}
void modify(int u)
{
int rt1=rt[u];
ret=1ll*ret*qpow(f[rt1],mod-)%mod;
update(dfn[u],(flag[rt1]^(dep[u]&))?:,,cnt,);
while(top[u]!=rt1)
{
mat p=query(dfn[top[u]],ed[top[u]],,cnt,);
int t=f[top[u]],sum=(p.a+p.b)%mod;
f[top[u]]=sum,u=fa[top[u]],a[u].a=1ll*a[u].a*qpow(t,mod-)%mod*sum%mod;
update(dfn[u],a[u].a,,cnt,);
}
mat p=query(dfn[rt1],ed[rt1],,cnt,);
f[rt1]=(p.a+p.b)%mod,ret=1ll*ret*f[rt1]%mod;
}
int main()
{
scanf("%d%d%d",&n,&L,&R);
pw[]=;for(int i=;i<=n;i++)pw[i]=2ll*pw[i-]%mod;
for(int i=,x,y;i<n;i++)scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x);
dfs1(),dfs();
build(,cnt,);
ans[n]=s[]-;
for(int i=n-;i;i--)
{
for(int j=;j<=q[i][];j++)modify(q[i][j]);
ans[i]=(s[]-ret+mod)%mod;
}
for(int i=L;i<=R;i++)printf("%d ",(ans[i]-ans[i-]+mod)%mod);
}
[ZJOI2019]Minimax搜索(线段树+动态DP+树剖)的更多相关文章
- 【BZOJ5210】最大连通子块和 树剖线段树+动态DP
[BZOJ5210]最大连通子块和 Description 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块 ...
- 「校内训练 2019-04-23」越野赛车问题 动态dp+树的直径
题目传送门 http://192.168.21.187/problem/1236 http://47.100.137.146/problem/1236 题解 题目中要求的显然是那个状态下的直径嘛. 所 ...
- 6.3 省选模拟赛 Decompose 动态dp 树链剖分 set
LINK:Decompose 看起来很难 实际上也很难 考验选手的dp 树链剖分 矩阵乘法的能力. 容易列出dp方程 暴力dp 期望得分28. 对于链的情况 容易发现dp方程可以转矩阵乘法 然后利用线 ...
- 5210: 最大连通子块和 动态DP 树链剖分
国际惯例的题面:这题......最大连通子块和显然可以DP,加上修改显然就是动态DP了......考虑正常情况下怎么DP:我们令a[i]表示选择i及i的子树中的一些点,最大连通子块和;b[i]表示在i ...
- BZOJ.1901.Dynamic Rankings(树状数组套主席树(动态主席树))
题目链接 BZOJ 洛谷 区间第k小,我们可以想到主席树.然而这是静态的,怎么支持修改? 静态的主席树是利用前缀和+差分来求解的,那么对于每个位置上的每棵树看做一个点,拿树状数组更新. 还是树状数组的 ...
- BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca
BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...
- [ZJOI2019]Minimax搜索
先求出根节点的权值\(w\).根据套路,我们对于每个\(k\),计算\(w(s)\leq k\)的方案数,差分得到答案.为了方便,接下来考虑计算概率而不是方案数. 可以发现,对于一个给定的有解的子集, ...
- bzoj 5210: 最大连通子块和【动态dp+树剖+线段树+堆】
参考:https://www.cnblogs.com/CQzhangyu/p/8632904.html 要开longlong的 首先看dp,设f[u]为必选u点的子树内最大联通块,p[u]为不一定选u ...
- BZOJ4712洪水——动态DP+树链剖分+线段树
题目描述 小A走到一个山脚下,准备给自己造一个小屋.这时候,小A的朋友(op,又叫管理员)打开了创造模式,然后飞到 山顶放了格水.于是小A面前出现了一个瀑布.作为平民的小A只好老实巴交地爬山堵水.那么 ...
随机推荐
- Nim游戏(尼姆博弈)
这里是尼姆博弈的模板,前面的博弈问题的博客里也有,这里单列出来. 有N堆石子.A B两个人轮流拿,A先拿.每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜.假设A B都非 ...
- c# 循环界面控件
在 Winform 开发中,窗体(Form)就像一个大容器,可以装各种各样的控件,包括 Panel控件.如果窗体是 Winform 中的最大的容器,那么 Panel控件可以算是老二,它专门用于软件界面 ...
- Zxing和QR Code生成和解析二维码
本文是学习慕课网课程<Java生成二维码>(http://www.imooc.com/learn/531)的笔记. 一.二维码的分类 线性堆叠式二维码.矩阵式二维码.邮政码. 二.二维码的 ...
- GPU 、APU、CUDA、TPU、FPGA介绍
购买显卡主要关注:显存.带宽和浮点运算数量 GPU :图形处理器(英语:Graphics Processing Unit,缩写:GPU),又称显示核心.视觉处理器.显示芯片,是一种专门在个人电脑. ...
- phpmyadmin拿网站shell
开门见山 1. 找到一个赌博网站,发现存在php探针界面,在下面输入密码尝试用弱口令进行连接,尝试是否成功 失败的结果是这样. 2. 成功! 3. 连接成功的,点击phpMyAdmin管理,进行弱口令 ...
- Python Learning Day3
爬虫练习 说是练习,实际是尝试了一些还没有具体了解的方式吧hhhhh' 基于urllib实现 import urllib.request import re url="https://www ...
- SDWebImage清理缓存
[[SDImageCache sharedImageCache] getSize]//计算缓存的大小,单位B float tmpSize = [[SDImageCache sharedImageCac ...
- 干货 | VPC之间的网络连通实践
随着公有云技术的日臻完善,越来越多的政府部门.事业单位.企业.个人将自己的IT系统部署在公有云之上.在公有云之上部署业务系统有一个特点,就是先要规划网络,有了网络以后,才能把一些相关的产品部署在网络里 ...
- 9.scrapy pycharm调试小技巧,请求一次,下次直接调试,不必每次都启动整个爬虫,重新请求一整遍
pycharm调试技巧:调试时,请求一次,下次直接调试,不必每次都启动整个爬虫,重新请求一整遍 [用法]cmd命令运行:scrapy shell 网址 第一步,cmd进行一次请求: scrapy sh ...
- 寒假day17-本周计划
完善人才的数据挖掘模块 结合当下疫情完成人才动态模块 修正人才标签部分 优化界面