今年年初的时候参加了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. PacBio下机数据如何看?

    一开始拿到三代测序的下机数据时,蒙了,readme ?三代测序的下机数据都有哪些,以及他们具体的格式是怎么样的(以sequel 平台为主). 测序过程 SMRTbell A adapter通用接头,两 ...

  2. 2018.09.15点名器(简单dp)

    描述 Ssoier在紧张的学习中,杜老师每天给他们传授精妙的知识. 杜老师为了活跃气氛,设计了一个点名器,这个点名器包含一个长度为M的数组(下标1开始),每个元素是一个oier的名字,每次点名的时候, ...

  3. 解决以showModalDialog打开的页面在提交表单时弹出新窗口的问题

    源代码如下: 父页面: window.showModalDialog("../readfile/readFile.jsp","","dialogWid ...

  4. 玩转git分支

    搞个代码的管理工具,居然不弄上分支啥的东西.这简直太low了.尤其是在使用了传说中得很牛X的Git的时候,尤其显得low.拿着青龙偃月刀当烧火棍子使,关公知道了还不重反人间教育你!? 远程分支 要说分 ...

  5. (KMP)剪花布条 -- hdu -- 2087

    http://acm.hdu.edu.cn/showproblem.php?pid=2087 剪花布条 Time Limit: 1000/1000 MS (Java/Others)    Memory ...

  6. 如何使用C++11实现C#属性概念设计

    目录(原创博客,版权所有,转载请注明出处 http://www.cnblogs.com/feng-sc) 1.概述 2.C#属性的概念  2.1.简单示例代码介绍C#中的属性  2.2.C++实现效果 ...

  7. 从0学习JQ

    转 张子秋的博客 为以后用到的时候好查询! 从零开始学习jQuery (一) 开天辟地入门篇 从零开始学习jQuery (二) 万能的选择器 从零开始学习jQuery (三) 管理jQuery包装集 ...

  8. SPFA穿越虫洞——负权回路得判断

    poj3259 题目大意:穿越虫洞可以回到过去(时间--)所以能不能让时间倒流呢,就是判断有没有负权回路这次尝试用SPFA算法,也可以复习一下链式前向星 准备工作,队列q,spfa算法得有点就在于这个 ...

  9. SoapUI5.1.2安装和破解教程

    一.SoapUI简介 soapui提供一个工具通过soap/http来检查,调用,实现web service和web service的功能/负载/符合性测试. 该工具既可作为一个桌面应用软件使用,也可 ...

  10. jenkins常用插件汇总

    jenkins常用插件汇总: Build-timeout Plugin:任务构建超时插件 Naginator Plugin:任务重试插件 Build User Vars Plugin:用户变量获取插件 ...