【pkuwc2018】 【loj2537】 Minmax DP+线段树合并
今年年初的时候参加了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$中有一个不存在时,考虑回这个dp式子:
$f[i][j]=f[s1][j] \times (s[s2][j-1]\times p+(s[s2][m]-s[s2][j])\times (1-p))$
不妨设x存在但y不存在,在这连续的一段中,我们发现这些f需要乘上的数(指$(s[s2][j-1]\times p+(s[s2][m]-s[s2][j])\times (1-p))$这一段),他们都是一样的,所以直接打一个区间乘的标记就可以了
(若$l==r$,则更新方式与上述$dp$式子相同,若$l≠r$,打个标记就行了)
考虑到线段树合并的时间复杂度为$O(n log n)$。所以这种方法是可以通过的。
完结撒花~~
1 #include<bits/stdc++.h> 2 #define L long long 3 #define MOD 998244353 4 #define M 300005 5 using namespace std; 6 7 L pow_mod(L x,L k){ 8 L ans=1; 9 while(k){ 10 if(k&1) ans=ans*x%MOD; 11 x=x*x%MOD; k>>=1; 12 } 13 return ans; 14 } 15 16 int lc[M*20]={0},rc[M*20]={0},root[M]={0},use=0; 17 L p[M*20]={0},tag[M*20]={0},gailv[M]={0}; 18 int l[M]={0},r[M]={0}; 19 20 void pushdown(int x){ 21 if(tag[x]!=1){ 22 if(lc[x]!=0) tag[lc[x]]=tag[lc[x]]*tag[x]%MOD,p[lc[x]]=p[lc[x]]*tag[x]%MOD; 23 if(rc[x]!=0) tag[rc[x]]=tag[rc[x]]*tag[x]%MOD,p[rc[x]]=p[rc[x]]*tag[x]%MOD; 24 tag[x]=1; 25 } 26 } 27 void pushup(int x){p[x]=(p[lc[x]]+p[rc[x]])%MOD;} 28 29 void updata(int &x,int l,int r,int k){ 30 if(!x) {x=++use; p[x]=tag[x]=1;} 31 if(l==r){p[x]=tag[x]=1; return;} 32 int mid=(l+r)>>1; 33 if(k<=mid) updata(lc[x],l,mid,k); 34 if(mid<k) updata(rc[x],mid+1,r,k); 35 pushup(x); 36 } 37 38 L nowp; 39 int solve(int x,int y,L xs1,L xs2,L ys1,L ys2){ 40 if(x==0&&y==0) return 0; 41 if(y==0){ 42 L upd=(xs1*nowp+xs2*(1-nowp+MOD))%MOD; 43 tag[x]=upd*tag[x]%MOD; 44 p[x]=upd*p[x]%MOD; 45 return x; 46 } 47 if(x==0){ 48 L upd=(ys1*nowp+ys2*(1-nowp+MOD))%MOD; 49 tag[y]=upd*tag[y]%MOD; 50 p[y]=upd*p[y]%MOD; 51 return y; 52 } 53 pushdown(x); pushdown(y); 54 L p1=p[lc[y]],p2=p[lc[x]]; 55 lc[x]=solve(lc[x],lc[y],xs1,(xs2+p[rc[y]])%MOD,ys1,(ys2+p[rc[x]])%MOD); 56 rc[x]=solve(rc[x],rc[y],(xs1+p1)%MOD,xs2,(ys1+p2)%MOD,ys2); 57 pushup(x); 58 return x; 59 } 60 61 void dfs(int x){ 62 if(l[x]) dfs(l[x]); 63 if(r[x]) dfs(r[x]); 64 if(!l[x]) return; 65 if(!r[x]) {root[x]=root[l[x]]; return;} 66 nowp=gailv[x]; 67 root[x]=solve(root[l[x]],root[r[x]],0,0,0,0); 68 } 69 L w[M]={0},hh[M]={0}; int m=0; 70 71 L ans=0; 72 void getans(int x,L l,L r){ 73 if(l==r){ 74 ans=(ans+l*hh[l]%MOD*p[x]%MOD*p[x])%MOD; 75 return; 76 } 77 pushdown(x); 78 L mid=(l+r)>>1; 79 getans(lc[x],l,mid); 80 getans(rc[x],mid+1,r); 81 } 82 83 int main(){ 84 //freopen("in.txt","r",stdin); 85 //freopen("out.txt","w",stdout); 86 int n; scanf("%d",&n); 87 for(int i=1;i<=n;i++){ 88 int x; scanf("%d",&x); 89 if(!l[x]) l[x]=i; 90 else r[x]=i; 91 } 92 L inv10000=pow_mod(10000,MOD-2); 93 for(int i=1;i<=n;i++){ 94 L x; scanf("%lld",&x); 95 if(l[i]) gailv[i]=x*inv10000%MOD; 96 else w[i]=x,hh[++m]=x; 97 } 98 sort(hh+1,hh+m+1); 99 for(int i=1;i<=n;i++) if(w[i]){ 100 int x; x=lower_bound(hh+1,hh+m+1,w[i])-hh; 101 updata(root[i],1,m,x); 102 } 103 dfs(1); 104 getans(root[1],1,m); 105 printf("%lld\n",ans); 106 }