今年年初的时候参加了PKUWC,结果当时这一题想了快$2h$都没有想出来....

哇我太菜啦....

昨天突然去搜了下哪里有题,发现$loj$上有于是就去做了下。

结果第一题我5分钟就把所有细节都想好了啊5555....

场上$60pts$消失...


显然,我们可以用$f[i][j]$表示节点$i$值为第$j$大的值的概率。

我们不难列出$dp$式子,$f[i][j]=f[s1][j] \times (s[s2][j-1]\times p+(s[s2][m]-s[s2][j])\times (1-p))$。

其中$s[i][j]=\sum_{k=0}{j} f[i][k]$。$s1$表示可以取到第j大的数的儿子,$s2$表示不能取到第$j$大的数的儿子。

显然,直接转移是$O(n^2)$的(我场上就写了这个)。

考虑如何进行优化。

题目中有一些特别优美的条件,比如说所有的数不会重复,树最多只有两个分叉。

如果某个树只有一个分叉的话,显然直接复制根即可,时间复杂度为$O(1)$。

考虑用线段树优化,考虑如何合并$s1$和$s2$这两棵树。

假设我们当前要合并的区间为$[l,r]$,且$x,y$分别为线段树中$s1,s2$用来表示区间$[l,r]$的节点。

对于该区间,我们用$xs1$来表示$s[s2][l-1]$,用$xs2$来表示$(s[s2][m]-s[s2][r])$。对于$s2$同理(暂且用$ys1,ys2$表示)。

递归的时候,若往线段树的左儿子递归,那么$xs2$就要加上$x$的右儿子的概率和,$ys2$同理。若线段树往右儿子递归,也同理。

然后当递归到$x$或$y$中有一个不存在时,直接用$xs1/2$,$ys1/2$更新即可。

(若$l==r$,则更新方式与上述$dp$式子相同,若$l≠r$,打个标记就行了)

考虑到线段树合并的时间复杂度为$O(n log n)$。所以这种方法是可以通过的。

完结撒花~~

 #include<bits/stdc++.h>
#define L long long
#define MOD 998244353
#define M 300005
using namespace std; L pow_mod(L x,L k){
L ans=;
while(k){
if(k&) ans=ans*x%MOD;
x=x*x%MOD; k>>=;
}
return ans;
} int lc[M*]={},rc[M*]={},root[M]={},use=;
L p[M*]={},tag[M*]={},gailv[M]={};
int l[M]={},r[M]={}; void pushdown(int x){
if(tag[x]!=){
if(lc[x]!=) tag[lc[x]]=tag[lc[x]]*tag[x]%MOD,p[lc[x]]=p[lc[x]]*tag[x]%MOD;
if(rc[x]!=) tag[rc[x]]=tag[rc[x]]*tag[x]%MOD,p[rc[x]]=p[rc[x]]*tag[x]%MOD;
tag[x]=;
}
}
void pushup(int x){p[x]=(p[lc[x]]+p[rc[x]])%MOD;} void updata(int &x,int l,int r,int k){
if(!x) {x=++use; p[x]=tag[x]=;}
if(l==r){p[x]=tag[x]=; return;}
int mid=(l+r)>>;
if(k<=mid) updata(lc[x],l,mid,k);
if(mid<k) updata(rc[x],mid+,r,k);
pushup(x);
} L nowp;
int solve(int x,int y,L xs1,L xs2,L ys1,L ys2){
if(x==&&y==) return ;
if(y==){
L upd=(xs1*nowp+xs2*(-nowp+MOD))%MOD;
tag[x]=upd*tag[x]%MOD;
p[x]=upd*p[x]%MOD;
return x;
}
if(x==){
L upd=(ys1*nowp+ys2*(-nowp+MOD))%MOD;
tag[y]=upd*tag[y]%MOD;
p[y]=upd*p[y]%MOD;
return y;
}
pushdown(x); pushdown(y);
L p1=p[lc[y]],p2=p[lc[x]];
lc[x]=solve(lc[x],lc[y],xs1,(xs2+p[rc[y]])%MOD,ys1,(ys2+p[rc[x]])%MOD);
rc[x]=solve(rc[x],rc[y],(xs1+p1)%MOD,xs2,(ys1+p2)%MOD,ys2);
pushup(x);
return x;
} void dfs(int x){
if(l[x]) dfs(l[x]);
if(r[x]) dfs(r[x]);
if(!l[x]) return;
if(!r[x]) {root[x]=root[l[x]]; return;}
nowp=gailv[x];
root[x]=solve(root[l[x]],root[r[x]],,,,);
}
L w[M]={},hh[M]={}; int m=; L ans=;
void getans(int x,L l,L r){
if(l==r){
ans=(ans+l*hh[l]%MOD*p[x]%MOD*p[x])%MOD;
return;
}
pushdown(x);
L mid=(l+r)>>;
getans(lc[x],l,mid);
getans(rc[x],mid+,r);
} int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n; scanf("%d",&n);
for(int i=;i<=n;i++){
int x; scanf("%d",&x);
if(!l[x]) l[x]=i;
else r[x]=i;
}
L inv10000=pow_mod(,MOD-);
for(int i=;i<=n;i++){
L x; scanf("%lld",&x);
if(l[i]) gailv[i]=x*inv10000%MOD;
else w[i]=x,hh[++m]=x;
}
sort(hh+,hh+m+);
for(int i=;i<=n;i++) if(w[i]){
int x; x=lower_bound(hh+,hh+m+,w[i])-hh;
updata(root[i],,m,x);
}
dfs();
getans(root[],,m);
printf("%lld\n",ans);
}

【pkuwc2018】 【loj2537】 Minmax DP+线段树合并的更多相关文章

  1. [BZOJ5461][LOJ#2537[PKUWC2018]Minimax(概率DP+线段树合并)

    还是没有弄清楚线段树合并的时间复杂度是怎么保证的,就当是$O(m\log n)$吧. 这题有一个显然的DP,dp[i][j]表示节点i的值为j的概率,转移时维护前缀后缀和,将4项加起来就好了. 这个感 ...

  2. LOJ2537. 「PKUWC2018」Minimax【概率DP+线段树合并】

    LINK 思路 首先暴力\(n^2\)是很好想的,就是把当前节点概率按照权值大小做前缀和和后缀和然后对于每一个值直接在另一个子树里面算出贡献和就可以了,注意乘上选最大的概率是小于当前权值的部分,选最小 ...

  3. BZOJ.5461.[PKUWC2018]Minimax(DP 线段树合并)

    BZOJ LOJ 令\(f[i][j]\)表示以\(i\)为根的子树,权值\(j\)作为根节点的概率. 设\(i\)的两棵子树分别为\(x,y\),记\(p_a\)表示\(f[x][a]\),\(p_ ...

  4. 【洛谷5298】[PKUWC2018] Minimax(树形DP+线段树合并)

    点此看题面 大致题意: 有一棵树,给出每个叶节点的点权(互不相同),非叶节点\(x\)至多有两个子节点,且其点权有\(p_x\)的概率是子节点点权较大值,有\(1-p_x\)的概率是子节点点权较小值. ...

  5. [PKUWC2018]Minimax [dp,线段树合并]

    好妙的一个题- 我们设 \(f_{i,j}\) 为 \(i\) 节点出现 \(j\) 的概率 设 \(l = ch[i][0] , r = ch[i][1]\) 即左儿子右儿子 设 \(m\) 为叶子 ...

  6. P6847-[CEOI2019]Magic Tree【dp,线段树合并】

    正题 题目链接:https://www.luogu.com.cn/problem/P6847 题目大意 \(n\)个点的一棵树上,每个时刻可以割掉一些边,一些节点上有果实表示如果在\(d_i\)时刻这 ...

  7. 洛谷P4577 [FJOI2018]领导集团问题(dp 线段树合并)

    题意 题目链接 Sol 首先不难想到一个dp,设\(f[i][j]\)表示\(i\)的子树内选择的最小值至少为\(j\)的最大个数 转移的时候维护一个后缀\(mx\)然后直接加 因为后缀max是单调不 ...

  8. BZOJ 5469: [FJOI2018]领导集团问题 dp+线段树合并

    在 dp 问题中,如果发现可以用后缀最大值来进行转移的话可以考虑去查分这个后缀最大值. 这样的话可以用差分的方式来方便地进行维护 ~ #include <bits/stdc++.h> #d ...

  9. LOJ2537 PKUWC2018 Minimax 树形DP、线段树合并

    传送门 题意:自己去看 首先可以知道,每一个点都有几率被选到,所以$i$与$V_i$的关系是确定了的. 所以我们只需要考虑每一个值的取到的概率. 很容易设计出一个$DP$:设$f_{i,j}$为在第$ ...

随机推荐

  1. SpringBoot集成篇(二) 异步调用Async

    什么是异步调用? 异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步调用则无需等待上一步程序执行完即可执行. 如何实现异步调用? 多线程, ...

  2. div和span元素的区别

    2个都是用来划分区间但是没有实际语义的标签,差别就在于div是块级元素,不会其他元素在同一行;span是内联元素,可以与其他元素位于同一行. DIV 和 SPAN 元素最大的特点是默认都没有对元素内的 ...

  3. 2018.07.08 hdu1394 Minimum Inversion Number(线段树)

    Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Ot ...

  4. Redis总结和提取常用的和重要的命令

    一:Redis的结构和其数据类型(注redis默认端口号是6379) 1)Redis可以部署多套(多个进程不同端口或直接部署在不同主机),每个Redis都可以有多个db,通过select来选择,默认的 ...

  5. BeautifulSoup基本步骤

    http://blog.csdn.net/kikaylee/article/details/56841789 ’BeautifulSoup是Python的一个库,最主要的功能就是从网页爬取我们需要的数 ...

  6. 9) 依赖查询 & 镜像站

    依赖查询 http://mvnrepository.com/ Maven仓库查询 http://search.maven.org 仓库 加上这两个,如果使用中央仓库 Eclipse 极有可能会卡死 & ...

  7. 2、WindowManager源码分析--最后一战

    在最后一站中,几乎所有的UI界面都是这个WindowManager管理的,那么他是如何调度的呢?我们来看看一些项目中的界面. 上面有登陆界面,专门管理登陆.战斗界面,用户界面,专门管理用户属性等等. ...

  8. Codeforces777C Alyona and Spreadsheet 2017-05-04 17:46 103人阅读 评论(0) 收藏

    C. Alyona and Spreadsheet time limit per test 1 second memory limit per test 256 megabytes input sta ...

  9. hdu 2845 Beans 2016-09-12 17:17 23人阅读 评论(0) 收藏

    Beans Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Subm ...

  10. hdu 4957 贪心破木桶接水大trick

    http://acm.hdu.edu.cn/showproblem.php?pid=4957 拿n只破的木桶去接水,每只木桶漏水速度为a[i],最后要得到b[i]单位的水,自来水的出水速度为V,木桶里 ...