zoukankan      html  css  js  c++  java
  • 【THUPC 2018】好图计数

    Problem

    Description

    这道题目非常简单,它甚至没有题目背景、没有任何故事。

    但为了能让你顺利理解题目,善良的 Yazid 将为你介绍一些概念。

    • 简单图:不存在重边、自环的图。(重边即为两条完全相同的边,自环即为两端点为同一节点的边)

    • 补图:一个图 (G) 的补图有与 (G) 完全相同的节点,且任意两点之间有边当且仅当他们在 (G) 中不相邻。

    我们归纳定义一个无向简单图是好的

    1. 一个单点是好的。

    2. 若干个好的图分别作为联通块所形成的图是好的。

    3. 一个好的图的补图是好的。

    总共 (T) 组数据。

    每组数据给定一个正整数 (n)

    (n) 个点的本质不同的好的图的数量对质数 (P) 取模的结果。

    两个好的图的被认为是本质不同的,当且仅当无论如何将一个图重标号,它都不能与另一个图完全相同。

    Range

    (Tle 233, nle 23333, 2^{29}< P< 2^{30}) ,且 (P) 为质数。

    Algorithm

    生成函数,(DP)

    Mentality

    典型的利用生成函数寻找性质。

    根据题目这个奇怪的定义,我们可以得到以下结论:

    不难发现,当超过一个点的时候,一个联通图要成为好图,必须依靠条件 (3)

    同时,对于一个不联通的图,它的补图一定是个联通图。证明很简单,对于任意两个联通块 (A)(B) ,在补图里,(A) 中的每个点都会向 (B) 中的每个点连边,则两个联通块自然就联通了。

    则我们的联通好图和不联通好图一定可以成对两两互补。

    所以设 (f_n)(n) 个点的好图个数,(g_n)(n) 个点的联通好图个数,则有:(f_n=2g_n)

    为了推式子比较方便,我们设 (f_0=1)

    不难发现,对于大小为 (k) 的联通好图,其能够组成的好图方案的生成函数为:

    [(sum_i x^{ik})^{g_k}=(1-x^k)^{-g_k} ]

    则我们可以列出 ({f_n}) 的生成函数 (F) 的式子:

    [F=prod_{k} (1-x^k)^{-g_k} ]

    发现右边是 (prod) 特别不好搞,于是考虑用 (ln) 拆成加法,然后再求导去掉 (ln)

    [lnF=sum_k (ln(1-x^k)^{-g_k})\ frac{F'}{F}=sum_k g_k*k*frac{x^{k-1}}{1-x^k}\ F'=F*sum_k g_k*k*frac{x^{k-1}}{1-x^k} ]

    接下来我们要推递推用的式子了:

    [[x^n]F'=(n+1)f_{n+1}=[x^n]F*(sum_k g_k*k*frac{x^{k-1}}{1-x^k})\ =sum_{i=0}^n f_i*[x^{n-i}](sum_k g_k*k*frac{x^{k-1}}{1-x^k}) ]

    对于 (frac{x^{k-1}}{1-x^k}) 来说,考虑 (frac{1}{1-x^k}=sum_{i>=0}x^{ik}) ,则有:

    [frac{x^{k-1}}{1-x^k}=sum_{i>=1}x^{ik-1} ]

    故可得:

    [[x^{n-i}](sum_k g_k*k*frac{x^{k-1}}{1-x^k})=sum_{k|(n-i+1)} kg_k ]

    然后我们设 (h_i=sum_{j|i} j*g_j)

    代回原式便有:

    [(n+1)f_{n+1}=sum_{i=0}^n f_i * h(n-i+1) ]

    发现由于在计算 (f_0*h(n+1)) 的时候式中包含未知的 (g_{n+1}=frac{f_{n+1}}{2}) ,所以将其移到左边去,则式子变为:

    [frac{(n+1)}{2}f_{n+1}=sum_{i=1}^n f_i * h(n-i+1)+sum_{k|(n+1),k<n+1} k*g(k) ]

    因为 (O2) 很猛,直接 (n^2) 卡常递推就可以过了。

    至于怎么卡常……什么 (FastMod) 加速取模都是假的,真正快到极致的就是不取模,用 (int128) 省去大量取模,你值得拥有。

    Code

    #include <cstdio>
    #include <iostream>
    using namespace std;
    #define LL long long
    #define go(G, x, i, v) 
      for (int i = G.hd[x], v = G.to[i]; i; v = G.to[i = G.nx[i]])
    #define inline __inline__ __attribute__((always_inline))
    inline LL read() {
      LL x = 0, w = 1;
      char ch = getchar();
      while (!isdigit(ch)) {
        if (ch == '-') w = -1;
        ch = getchar();
      }
      while (isdigit(ch)) {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar();
      }
      return x * w;
    }
    
    const int Max_n = 3e4 + 5;
    int T, mod, n;
    int f[Max_n], g[Max_n], h[Max_n];
    
    namespace Init {
    int ksm(int a, int b = mod - 2) {
      int res = 1;
      for (; b; b >>= 1, a = (LL)a * a % mod)
        if (b & 1) res = (LL) res * a % mod;
      return res;
    }
    void main() {
      n = 23333;
      for (int i = 1; i <= n; i++) {
        __int128 t = 0;
        for (int j = 1; j < i; j++) t += (LL)f[j] * h[i - j];
        f[i] = 2ll * (f[i] + t % mod) * ksm(i) % mod;
        g[i] = (LL)ksm(2) * f[i] % mod + (i == 1), f[i] += (i == 1);
        for (int j = i; j <= n; j += i) {
          (h[j] += (LL)i * g[i] % mod) %= mod;
          if (j > i) (f[j] += (LL)i * g[i] % mod) %= mod;
        }
      }
    }
    }  // namespace Init
    
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("6389.in", "r", stdin);
      freopen("6389.out", "w", stdout);
    #endif
      T = read(), mod = read();
      Init::main();
      while(T--) {
        n = read();
        printf("%d
    ", f[n]);
      }
    }
    
  • 相关阅读:
    Nginx 日志格式配置介绍
    Java Spring cron表达式使用详解
    Elasticsearch Query DSL
    Elasticsearch Search APIs
    网易2016研发工程师编程题:小易的升级之路
    2016奇虎360研发工程师内推笔试编程题:找镇长
    2016奇虎360研发工程师内推笔试编程题:找到字符串第一个只出现一次的字符
    lintcode: 最长无重复字符的子串
    lintcode :同构字符串
    lintcode : 跳跃游戏
  • 原文地址:https://www.cnblogs.com/luoshuitianyi/p/12890496.html
Copyright © 2011-2022 走看看