• 湖南集训day6


    难度:☆☆☆☆☆☆☆☆

    /*
    对于第一问:f[i][j]表示前i个数,当前黑板上的数为j的概率
    当前有三种情况
    1.当前数不是j的倍数—>黑板上的数字改变。
    2.当前数是j的倍数且当前数在前i个数中(已经选过)
    3.当前数是j的倍数且没有选过
    转移:f[i+1][j]=((j的倍数个数-i)*f[i][j]+f[i][gcd(j,k)])  的平均值  j的倍数个数-i是没选过的j的倍数。
    对于第二问,考虑博弈论中sg函数。可知sg[i][1]二维含义同f数组)必定为0(最后黑板上剩下1必败)  sg[n][i]=0(选完了必败) 同样枚举上述三种情况,取后续状态mex值即可。
    */
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define eps 1e-8
    #define N 1000
    using namespace std;
    inline int gcd(int a, int b)
    {
        return b == 0 ? a : gcd(b, a % b);
    }
    int sg[N + 5][N + 5], g[N + 5][N + 5], f[N + 5], n, a[N + 5];
    double dp[N + 5][N + 5], ans = 0;
    bool getsg(int x, int y)
    {
        if (x == 1) return 1;
        if (sg[x][y] != -1) return sg[x][y];
        bool flag = 1;
        if (f[x] > y) flag &= getsg(x, y + 1);
        for (int i = 1; i <= n; i++)
            if (g[x][i] != x)
                flag &= getsg(g[x][i], y + 1);
        sg[x][y] = !flag;
        return sg[x][y];
    }
    int main()
    {
        freopen("cards.in", "r", stdin);
        freopen("cards.out", "w", stdout);
        scanf("%d", &n);
        int mx = 0;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", a + i);
            mx = max(mx, a[i]);
            g[0][i] = a[i];
        }
        for (int i = 1; i <= mx; i++)
            for (int j = 1; j <= n; j++)
                f[i] += (a[j] % i == 0), g[i][j] = gcd(i, a[j]);
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++)
            for (int j = 0; j <= mx; j++)
                if (dp[i - 1][j] > eps)
                {
                    dp[i][j] += dp[i - 1][j] * (f[j] - i + 1) / (n - i + 1);
                    for (int k = 1; k <= n; k++)
                        if (g[j][k] != j)
                        {
                            if (g[j][k] != 1)
                                dp[i][g[j][k]] += dp[i - 1][j] / (n - i + 1);
                            else
                                ans += (i + 1 & 1) * dp[i - 1][j] / (n - i + 1);
                        }
                }
        if (n & 1)
            for (int j = 0; j <= mx; j++) ans += dp[n][j];
        printf("%.9lf ", ans);
        memset(sg, -1, sizeof(sg));
        if (getsg(0, 0)) puts("1.000000000");
        else puts("0.000000000");
        return 0;
    }

    /*
    对于60~80分,可以n^2暴力,断掉每条边时,O(N)求每个部分的直径,然后相乘。
    
    正解:倒序加边,考虑两棵树合并的时候新直径一定是原来两个直径四个断点任意两个的路径。
    所以可以首先求LCA倍增处理两点间路径,然后求最大。更新答案要用逆元。 
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 100007
    #define mod 1000000007
    #define ll long long
    
    using namespace std;
    int a[N],dep[N],sum[N],fa[N][20],head[N],del[N];
    int D[N][2],ans[N],f[N],len[N],end[2];
    int n,m,pre,cnt;
    struct edge{
        int u,v,net;
    }e[N<<1];
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    inline void add(int u,int v)
    {
        e[++cnt].u=u;e[cnt].v=v;e[cnt].net=head[u];head[u]=cnt;
    }
    
    void dfs(int u,int from)
    {
        fa[u][0]=from;dep[u]=dep[from]+1;
        sum[u]=sum[from]+a[u];
        for(int i=1;i<=19;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
        for(int i=head[u];i;i=e[i].net)
        {
            int v=e[i].v;
            if(v!=from)dfs(v,u);
        }return;
    }
    
    int find(int x)
    {
        return x==f[x]?x:f[x]=find(f[x]);
    }
    
    int ksm(int a,int b)
    {
        int res=1;
        while(b)
        {
            if(b&1) res=1ll*res*a%mod;
            b>>=1;a=1ll*a*a%mod;
        }return res%mod;
    }
    
    int lca(int u,int v)
    {
        if(dep[u]<dep[v]) swap(u,v);
        int t=dep[u]-dep[v];
        for(int i=0;i<20;i++)
          if(t&(1<<i)) u=fa[u][i];
        if(u==v) return u;
        for(int i=19;i>=0;i--)
        {
            if(fa[u][i]!=fa[v][i])
            {
                u=fa[u][i];
                v=fa[v][i];
            }
        }return fa[u][0];
    }
    
    int getlen(int u,int v)
    {
        int L=lca(u,v);
        return sum[u]+sum[v]-2*sum[L]+a[L];
    }
    
    int main()
    {
        freopen("forest.in","r",stdin);
        freopen("forest.out","w",stdout);
        int x,y;
        n=read();pre=1;
        for(int i=1;i<=n;i++)
        {
            a[i]=read();f[i]=i;
            pre=(ll)pre*a[i]%mod;
            D[i][0]=D[i][1]=i;len[i]=a[i];
        }
        for(int i=1;i<n;i++)
        {
            x=read();y=read();
            add(x,y);add(y,x);
        }
        dfs(1,0);int t=n;ans[n]=pre;
        for(int i=1;i<n;i++) del[i]=read();
        
        for(int i=n-1;i;i--)
        {
            int id=del[i],u=e[id*2-1].u,v=e[id*2-1].v;
            u=find(u);v=find(v);
            if(len[u]<len[v]) swap(u,v);
            int tmax=len[u];
            for(int j=0;j<2;j++) end[j]=D[u][j];
            for(int j=0;j<2;j++)
              for(int k=0;k<2;k++)
                {
                    int l=getlen(D[u][j],D[v][k]);
                    if(l>tmax)
                    {
                        tmax=l;
                        end[0]=D[u][j];end[1]=D[v][k];
                    }
                }
                
            pre=(ll) pre*ksm(len[u],mod-2)%mod;
            pre=(ll) pre*ksm(len[v],mod-2)%mod;
            f[v]=u;len[u]=tmax;
            for(int j=0;j<2;j++) D[u][j]=end[j];
            pre=(ll) pre*len[u]%mod;
            ans[--t]=pre;
        }
        for(int i=1;i<=n;i++) printf("%d
    ",ans[i]);
        return 0;
    }

    /*
    这题确实是恶心,并且貌似没有部分分......
    让我们来瞻仰一下std做法
    考虑两列的情况。若两列颜色分别为A,B,则A独有的颜色就是A—A∩B ,B同理。
    若是多列那还是设两边两列为A,B,中间多列为C,那根据题目结论可以知道C一定是A∩B的子集。枚举A中独有颜色个数,B中独有颜色个数与A中相同。若有j独有,i共有C(K,i)*C(k-i+1,j)*C(k-i-j,j)
    因为每次选择都必须是恰好那些颜色,不能少,所以用总方案数减去不是恰好的就可以了。 
    */ 
    # include<iostream>
    # include<cstdio>
    # include<cstring>
    # include<cstdlib>
    
    using namespace std;
    const int pp=1000000007;
    int c[2008][2008],f[2008],p[2008],ni[2008];
    int n,m,k,nn;
    
    inline int power(int x,int n)
    {
        int ans=1,tmp=x;
        while (n)
        {
              if (n&1) ans=(long long)ans*tmp%pp;
              tmp=(long long)tmp*tmp%pp;n>>=1;
        }    
        return ans;
    }
    
    void Count_c()
    {
         for (int i=0;i<=nn;i++) c[i][0]=1;
         for (int i=1;i<=nn;i++)
          for (int j=1;j<=i;j++)
          {
              c[i][j]=c[i-1][j-1]+c[i-1][j];
              if (c[i][j]>=pp) c[i][j]-=pp;
          }
    }
    
    void Count_p()
    {
         int mm=(m-2)*n;
         for (int i=0;i<=nn;i++)
          p[i]=power(i,mm);
    }
    
    void Count_f()
    {
         f[0]=0;f[1]=1;
         for (int i=2;i<=nn;i++)
         {
             f[i]=power(i,n);
             for (int j=1;j<i;j++)
             {
                 f[i]-=(long long)f[j]*c[i][j]%pp;
                 if (f[i]<=-pp) f[i]+=pp;
             }
             if (f[i]<0) f[i]+=pp;
         }
    }
    
    void Count_ni()
    {
         ni[1]=1;
         for (int i=2;i<=nn;i++)
         ni[i]=power(i,pp-2);
    }
    
    int main()
    {
        freopen("photo.in","r",stdin);
        freopen("photo.out","w",stdout);
        scanf("%d%d%d",&n,&m,&k);
        nn=min(n,k);
        if (m==1)
           printf("%d
    ",power(k,n));
        else
        {
            Count_c();Count_p();
            Count_f();Count_ni();
            long long tmp=1,tmp1=1,sum=0,sum1;
            for (int s=1;s<=nn;s++)
            {
                tmp=tmp*ni[s]%pp;
                tmp=tmp*(k-s+1)%pp;
                tmp1=1;sum1=0;
                for (int j=0;j<=s;j++)
                {
                    sum1+=tmp1*c[s][s-j]%pp*p[s-j]%pp;
                    if (sum1>=pp) sum1-=pp;
                    tmp1=tmp1*ni[j+1]%pp; 
                    if (k-s<j+1) break;
                    tmp1=tmp1*(k-s-j)%pp;
                }
                sum+=tmp*f[s]%pp*f[s]%pp*sum1%pp;
                if (sum>=pp) sum-=pp;
            }
            printf("%d
    ",sum);
        }
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    折花枝,恨花枝,准拟花开人共卮,开时人去时。 怕相思,已相思,轮到相思没处辞,眉间露一丝。
  • 相关阅读:
    在 Linux 下查看硬件配置
    对于 ASP.NET 在 IIS 上的一些高并发处理配置
    处理 目标主机SSH服务存在RC4、CBC或None弱加密算法 的问题
    处理 Windows Server 中 CVE-2016-2183(SSL/TLS) 漏洞的方法
    在 Windows Server 2008 R2下部署 asp.net core 3.1 网站遇到的问题
    在 Windows Server 2012 安装最新版 SSMS 遇到错误 0x80070005 -Acess Denied
    在 CentOS 中安装 7zip
    在 npm run build 时遇到报错 [BABEL] No "exports" main defined in
    【性能项目实战:k8s+微服务】热门测试技术,提升职场竞争力(持续更新中。。。)
    windows下安装skywalking8.6.0(用于本地开发调试代码)
  • 原文地址:https://www.cnblogs.com/L-Memory/p/7650254.html
Copyright © 2020-2023  润新知