zoukankan      html  css  js  c++  java
  • 序列Counting

      模拟的时候切掉的,感觉这道题还是很好的。(虽然T1期望啥也不会积分瞎搞拿了个二十五分,T2好不容易搞了个字符串dp,最后数组还开小了……不过终于狗进前五)

      题面描述:

      构建一个N个点的有向图G,初始没有任何边。接下来构建一个长度为E的边的序列A,序列中每条边都是满足1≤s,t≤N且s≠t的有向边(s,t),且序列中的边互不相同。按照顺序把这些边加入到G中,每次加入后计算当前图的强连通分量个数并记录下来,得到一个新的长度为E的正整数序列B。如果两个边的序列得到的B相同则称它们本质相同。

      请问有多少种本质不同的边的序列,你只要求出答案对10^9+7取模后的结果。

      很显然不能一遍一遍跑Tarjan(笑哭)

      我们先分析一下这道题:

      首先我们考虑一个合法的B序列,一定是单调不增的。我们将一条链作为图的主干考虑,对于我们加的每一条边,我们可以了考虑是继续拓展这条链,还是在原有的链的基础之上随便连边。那么常规的我们设计一个状态 f [ i ] [ j ] 表示前 i 条边,现在有 j 个强连通分量的方案数。

      那么很显然对于 i 和 j 是有一些关 系的,不是任何一对 i , j 都是合法的。

      那么我们考虑一波:

      假设现在序列B被分为 k 段,每段的大小相等(连了一些废边),那么作为在链的基础上加的边至少有 k-1 条,那么也就是说在链上的边最多有 i - k +1条。

      设Bi = j ,也就是说现在有 j 个强连通分量,那么显然用到链上的边最少为 n - j 条(一个环,j - 1 个孤立点,n - j + 1 个环上点,n - j + 1 条环上边,其中有一条反向的边)。

      那么显然我们要满足 n - j <= i - k + 1 ,多、也就是说对于一对 i,j 我们必须满足 i + j >= n + k -1 才行。

      这说明情况是否合法收到 k 的限制。也就是说对于一对 i , j ,其对应的情况并不都合法,i ,j 的合法情况是其所有情况的一个子集。

      那么我们就要加一维限制,f [ i ] [ j ] [ k ] 代表对于加的 i 条边,有 j 个强连通分量,分成了 k 段的方案数。

      然后我们又发现,由于这是简单图,所以当强连通分量个数为 j 时,边数是有上限的,最多为 ( n - j + 1 ) * ( n - 1 ) + ( j - 1 ) * ( j - 2 ) / 2 。

      所以也必须满足条件 i <= ( n - j + 1 ) * ( n - 1 ) + ( j - 1 ) * ( j - 2 ) / 2 。

      那么 f [ i ] [ j ] [ k ] 就很好转移了,只要考虑第 i 条边是否是废边即可 : f [ i ] [ j ] [ k ] += f [ i - 1 ] [ h ] [ k - 1 ] + f [ i - 1 ] [ j ] [ k ] 。  

      通过维护前缀和为我们可以把复杂度降到 n3,但是显然我们还是不满意的>///<

      我们观察 i + j >= n + k -1 ,发现在 i >= 2n 的时候总是成立的,所以我们就不需要 k 这一维了,f [ i ] [ j ] 就可以了,转移也一样。

      这样就大功告成啦!当然由于模拟的时候 n 是100,所以不需要后来的二维转移,三维n4就是可以接受的。

      代码:

      1.模拟代码:

    #include <iostream>
    #include <cstdio>
    #define maxn 102
    #define mod 1000000007
    using namespace std;
    int dp[maxn * maxn][maxn][maxn];
    int add(int x, int y)
    {
        x += y;
        return x >= mod ? x - mod : x;
    }
    int main()
    {
        int n;
        cin >> n;
        for (int i = n; i >= 0; -- i)
            dp[0][i][0] = 1;
        for (int e = 1; e <= n * (n - 1); ++ e)
        {
            int ans = 0;
            for (int v = 1; v <= n; ++ v)
                for (int m = 0; m < n; ++ m)
                    if (e >= (n - v) + m && e <= (n - v + 1) * (n - 1) + (v - 1) * (v - 2) / 2)
                    {
                        dp[e][v][m] = add(dp[e][v][m], (mod + dp[e - 1][v][m] - dp[e - 1][v + 1][m]) % mod);
                        dp[e][v][m] = add(dp[e][v][m], dp[e - 1][v + 1][m - 1]);
                        ans = add(ans, dp[e][v][m]);
                    }
            for (int m = 0; m <= n; ++ m)
                for (int v = n - 1; v >= 0; -- v)
                    dp[e][v][m] = add(dp[e][v][m], dp[e][v + 1][m]);
            printf("%d ",ans);
        }
        return 0;
    }

      2. n <= 400 二维优化代码:

    #include<cstdio>
    #include<iostream>
    #include<queue>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #include<cstring>
    using namespace std;
    #define mod 1000000007
    const int maxn = 500;
    int lim[maxn], f[2][maxn][maxn], sf[2][maxn][maxn], g[2][maxn], sg[2][maxn],ans[maxn*maxn];
    int n;
    int main()
    {
        scanf("%d",&n);
        for (int i = 1; i <= n; i++)
            lim[i] = (n - i + 1) * (n - 1) + (i - 1) * (i - 2) / 2;
        f[1][n][1] = ans[1] = 1;
        for (int i = 1; i <= n; i++)
            sf[1][i][1] = 1;
        for (int i = 2; i <= min(n * (n - 1), n << 1); i++)
        {
            int op = i & 1;
            for (int j = 1; j <= n; j++)
                for (int k = 1; k <= n; k++)
                    f[op][j][k] = 0;
            for (int j=1; j<=n; j++)
                if (i<=lim[j])
                    for (int k = 1; k <= n; k++)
                        if (i + j >= n + k - 1)
                            f[op][j][k] = (f[op ^ 1][j][k] + sf[op ^ 1][j + 1][k - 1]) % mod;
            for (int j = n; j >= 1; j--)
                for (int k = 1; k <= n; k++)
                {
                    sf[op][j][k] = (sf[op][j + 1][k] + f[op][j][k]) % mod;
                    ans[i] = (ans[i] + f[op][j][k]) % mod;
                }
        }
        for (int j = 1; j <= n; j++)
            for (int k = 1; k <= n; k++)
                g[0][j] = (g[0][j] + f[0][j][k]) % mod;
        for (int j = n; j >= 1; j--)
            sg[0][j] = (sg[0][j + 1] + g[0][j]) % mod;
        for (int i = (n << 1) + 1; i <= n * (n - 1); i++)
        {
            int op = i & 1;
            for (int j = 1; j <= n; j++)
                g[op][j] = 0;
            for (int j = 1; j <= n; j++)
                if (i <= lim[j])
                    g[op][j] = sg[op ^ 1][j];
            for (int j = n; j >= 1; j--)
            {
                sg[op][j] = (sg[op][j + 1] + g[op][j]) % mod;
                ans[i] = (ans[i] + g[op][j]) % mod;
            }
        }
        for (int i = 1; i <= n * (n - 1); i++)
            printf("%d ", ans[i]);
        printf("
    ");
        return 0;
    }

       注:感谢 yfl 和 lyn 以及 wty 大佬们的悉心讲解,第一题部分分积分算法已经明白:

      对于一条链的情况,len(p)=sigama(i=1,边的个数)* sigama P * ( xi ) = sigama(i=1,边的个数)* 1/n * ( 0 + 1 ) * n / 2= ( n - 1 ) / 2 。

      

  • 相关阅读:
    启动VMware出现报错:The VMware Authorization Service is not running
    CentOS8安装SQLServer2019
    CentOS8安装Tomcat
    CentOS8安装java环境
    手把手0基础Centos下安装与部署paddleOcr 教程
    redis反向代理docker容器中的rabbit mq服务
    MQTT 4 ——MQTT的Spring Mvc 配置接收字节流数据
    MQTT 3 ——MQTT与Spring Mvc整合
    MQTT 2——服务端安装与客户端测试
    MQTT 1——物联网集成项目技术选型与说明
  • 原文地址:https://www.cnblogs.com/popo-black-cat/p/11122798.html
Copyright © 2011-2022 走看看