zoukankan      html  css  js  c++  java
  • loj 2542 随机游走 —— 最值反演+树上期望DP+fmt

    题目:https://loj.ac/problem/2542

    因为走到所有点的期望就是所有点期望的最大值,所以先最值反演一下,问题变成从根走到一个点集任意一点就停止的期望值;

    设 ( f[x] ),则 ( f[x] = frac{f[fa]+1+sumlimits_{v in son} (f[v]+1)}{d[x]} ),其中 ( d[x] ) 是 ( x ) 的度数;

    因为其实只和 ( fa ) 有关,所以套路是设 ( f[x] = K[x] * f[fa] + B[x] ),推一推就可以树形DP求 ( K[x] , B[x] ),这好像叫“树上高斯消元”?!

    因为走到集合内任意一个点就停止,所以 ( K[x] = B[x] = 0 ( x in S ) ),( f[rt] ) 即 ( B[rt] ) 就是答案;

    然后高维前缀和求子集的 ( f ) 和,注意最值反演的符号要一开始就带上。

    代码如下:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int rd()
    {
      int ret=0,f=1; char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return f?ret:-ret;
    }
    int const xn=20,xm=(1<<18)+5,mod=998244353;
    int n,rt,sid[xn][xn],bin[xn],cnt[xm],K[xn],B[xn],d[xn],f[xm];
    ll pw(ll a,int b){a%=mod; ll ret=1; for(;b;b>>=1,a=a*a%mod)if(b&1)ret=ret*a%mod; return ret;}
    int upt(int x){while(x>=mod)x-=mod; while(x<0)x+=mod; return x;}
    void dfs(int x,int fa,int s)
    {
      if(s&bin[x-1]){K[x]=B[x]=0; return;}
      int ks=0,bs=0;
      for(int u=1;u<=n;u++)
        if(sid[x][u]&&u!=fa)
          dfs(u,x,s),ks=upt(ks+K[u]),bs=upt(bs+B[u]);
      K[x]=pw(d[x]-ks,mod-2); B[x]=(ll)(bs+d[x])*pw(d[x]-ks,mod-2)%mod;
    }
    int main()
    {
      n=rd(); int Q=rd(); rt=rd();
      for(int i=1,x,y;i<n;i++)x=rd(),y=rd(),sid[x][y]=sid[y][x]=1,d[x]++,d[y]++;
      bin[0]=1; for(int i=1;i<=n;i++)bin[i]=(bin[i-1]<<1);
      for(int s=1;s<bin[n];s++)cnt[s]=cnt[s>>1]+(s&1);
      for(int s=1;s<bin[n];s++)
        {
          memset(K,0,sizeof K); memset(B,0,sizeof B);
          dfs(rt,0,s); f[s]=upt(((cnt[s]&1)?1:-1)*B[rt]);//
        }
      for(int i=1;i<=n;i++)
        for(int s=0;s<bin[n];s++)
          if(s&bin[i-1])f[s]=upt(f[s]+f[s^bin[i-1]]);
      for(int i=1,k,s;i<=Q;i++)
        {
          k=rd(); s=0;
          for(int j=1,x;j<=k;j++)x=rd(),s|=bin[x-1];
          printf("%d
    ",f[s]);
        }
      return 0;
    }
  • 相关阅读:
    常见总结
    手机号中间四位替换为****
    写给30岁之前的我
    顺序栈ADT简单的实现
    快速排序
    关于存储类的例子总结
    非有序的静态查找表的查找某个元素的算法
    有序的静态查找表的折半(二分)查找算法
    【项目】项目125
    【项目】项目132
  • 原文地址:https://www.cnblogs.com/Zinn/p/10279681.html
Copyright © 2011-2022 走看看