zoukankan      html  css  js  c++  java
  • 【BZOJ3456】【CDQ分治+FNT】城市规划

    试题来源
      2013中国国家集训队第二次作业
    问题描述
      刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
      刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通.
      为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
      好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n个点的简单(无重边无自环)无向连通图数目.
      由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^ 21 + 1)即可.
    输入格式
      仅一行一个整数n(<=130000)
    输出格式
      仅一行一个整数, 为方案数 mod 1004535809.
    样例输入
    3
    样例输出
    4
    样例输入
    4
    样例输出
    28
    样例输入
    100000
    样例输出
    829847355
    数据规模和约定
      对于 20%的数据, n <= 10
      对于 40%的数据, n <= 1000
      对于 60%的数据, n <= 30000
      对于 80%的数据, n <= 60000
      对于 100%的数据, n <= 130000
    【分析】
    非常巧妙的题目。
    首先可以推递推式,用f(n) = 2^C(n,2) - sigma{(2 ^ C(n - i, 2)) * C(n - 1, i - 1) * f(i) |  1<= i <n  }
    相当于选定一个点不动,逐步扩大该点所在的联通快的大小。
    可以保证不会出现重复。
    经过化简将C(n-1,i-1)中的(n-1)!提出来以后,可以通过一种非常巧妙的构造卷积的方式解决问题(详见代码)。
    为什么可以用FFT?。。。。FFT怎么取模啊,不会QAQ。
      1 /*
      2 宋代朱敦儒
      3 《西江月·世事短如春梦》
      4 世事短如春梦,人情薄似秋云。不须计较苦劳心。万事原来有命。
      5 幸遇三杯酒好,况逢一朵花新。片时欢笑且相亲。明日阴晴未定。 
      6 */
      7 #include <cstdio>
      8 #include <cstring>
      9 #include <algorithm>
     10 #include <cmath>
     11 #include <queue>
     12 #include <vector>
     13 #include <iostream>
     14 #include <string>
     15 #include <ctime>
     16 #define LOCAL
     17 const int MAXN = 130000 * 2 * 2 + 10;
     18 const long long MOD = 1004535809;//费马数论变换的费马素数 
     19 long long G = 3;//元根 
     20 using namespace std;
     21 typedef long long ll;
     22 ll x1[MAXN], x2[MAXN];
     23 ll data[MAXN], Ans[MAXN];
     24 ll f[MAXN], I[MAXN], nI[MAXN], C[MAXN];
     25 ll inv2[MAXN], wn[MAXN], Inv[MAXN];
     26 
     27 ll pow(ll a, ll b){
     28    if (b == 0) return 1 % MOD;
     29    if (b == 1) return a % MOD;
     30    ll tmp = pow(a, b / 2);
     31    if (b % 2 == 0) return (tmp * tmp) % MOD;
     32    else return ((tmp * tmp) % MOD * (a % MOD)) % MOD;
     33 }
     34 ll exgcd(ll a, ll b, ll &x, ll &y){
     35    if (b == 0){x = 1ll; y = 0; return a;}
     36    ll tmp = exgcd(b, a % b, y, x);
     37    y -= x * (a / b);
     38    return tmp;
     39 }
     40 ll inv(ll a, ll p){
     41    ll x, y;
     42    ll tmp = exgcd(a, p, x, y);
     43    return ((x % MOD) + MOD) % MOD; 
     44 }
     45 void change(ll *x, int len, int loglen){
     46      for (int i = 0; i < len; i++){
     47          int t = i, k = 0, tmp = loglen;
     48          while (tmp--) {k = (k << 1) + (t & 1); t >>= 1;}
     49          if (k < i) swap(x[i], x[k]);
     50      }
     51      return;
     52 }
     53 void FNT(ll *x, int len, int loglen, int type){
     54      if (type) change(x, len, loglen);
     55      int t;
     56      t = (type ? 1: (1 << loglen));
     57      for (int i = 0; i < loglen; i++){
     58          if (!type) t >>= 1;
     59          int l = 0, r = l + t;
     60          while (l < len){
     61                ll a, b;
     62                ll tmp = 1ll, w = wn[t];
     63                if (!type) w = Inv[t];
     64                for (int j = l; j < l + t; j++){
     65                    if (type){
     66                       a = x[j] % MOD;
     67                       b = (x[j + t] * (tmp % MOD)) % MOD;
     68                       x[j] = (a + b) % MOD;
     69                       x[j + t] = ((a - b) % MOD + MOD) % MOD; 
     70                    }else{
     71                       a = (x[j] + x[j + t]) % MOD;
     72                       b = ((((x[j] - x[j + t]) % MOD + MOD) % MOD) * tmp) % MOD;
     73                       x[j] = a;
     74                       x[j + t] = b;
     75                    }
     76                    tmp = (tmp * w) % MOD;
     77                }
     78                l = r + t;
     79                r = l + t;
     80          } 
     81          if (type) t <<= 1;
     82      }
     83      if (!type){
     84         change(x, len, loglen);
     85         for (int i = 0; i < len; i++) x[i] = (x[i] % MOD * inv(len, MOD)) % MOD;
     86      }
     87 }
     88 //CDQ分治 
     89 void solve(int l, int r){
     90      if (l == r){
     91         //T为组合数的, I[l]为阶乘的模 
     92         f[l] = (C[l] - (I[l - 1] * f[l]) % MOD + MOD) % MOD;
     93         return;
     94      }
     95      int mid = (l + r) >> 1;
     96      solve(l, mid);
     97      int len = max(mid - l + 1, r - mid), loglen = 1;
     98      while ((1<<loglen) < ((r - l + 1) << 1)) loglen++;//要把卷积分成两个部分
     99      
    100      for (int i = 0; i < (1<<loglen); i++) x1[i] = x2[i] = 0;
    101      for (int i = l; i <= mid; i++) x1[i - l] = (nI[i - 1] * f[i]) % MOD;
    102      for (int i = 1; i <= r - l; i++) x2[i] = (C[i] * nI[i]) % MOD;
    103      
    104      FNT(x1, (1<<loglen), loglen, 1);
    105      FNT(x2, (1<<loglen), loglen, 1);
    106      for (int i = 0; i < (1<<loglen); i++) x1[i] = (x1[i] * x2[i]) % MOD;
    107      FNT(x1, (1<<loglen), loglen, 0);
    108      for (int i = mid + 1; i <= r; i++) f[i] = (f[i] + x1[i - l] % MOD) % MOD;
    109      solve(mid + 1, r);
    110 }
    111 int n;
    112 void init(){
    113      scanf("%d", &n);
    114      inv2[0] = 1;
    115      inv2[1] = inv(2, MOD);
    116      //预处理2的阶乘的逆元 
    117      for (int i = 2; i <= n; i++) inv2[i] = (inv2[i - 1] * inv2[1]) % MOD;
    118      for (int i = 1; i <= 2 * n; i++){
    119          if (i != 1 && (MOD - 1) / (2 * i) == (MOD - 1) / (2 * (i - 1))){
    120             wn[i] = wn[i - 1];
    121             Inv[i] = Inv[i - 1];
    122          }else{
    123             wn[i] = pow(G, (MOD - 1) / (2 * i)) % MOD;
    124             Inv[i] = inv(wn[i], MOD) % MOD;
    125          }
    126      }
    127      I[0] = nI[0] = 1;//分别代表阶乘和阶乘的-1次方 
    128      for (int i = 1; i <= n; i++){
    129          I[i] = (long long)(i * I[i - 1]) % MOD;
    130          nI[i] = (inv((long long)i, MOD) * nI[i - 1]) % MOD;
    131          //printf("%lld
    ", nI[i]);
    132      } 
    133      //预处理组合数2^C(n, 2)
    134      for (int i = 1; i <= n; i++){
    135          C[i] = pow(2ll, (long long)((long long)i * (i - 1)) / 2) % MOD;
    136          //printf("%lld
    ", C[i]);
    137      }
    138 }
    139 
    140 
    141 int main() {
    142    
    143     init();
    144     solve(1, n);
    145     printf("%lld
    ", f[n]);
    146     return 0;
    147 }
    View Code
  • 相关阅读:
    Dynamics CRM2016 Web API之更新记录
    opencv基础笔记(1)
    HTML5开发移动web应用——SAP UI5篇(6)
    就算你不是电商,你应该为你的电商朋友好好看看这篇文章
    xode5.1.1设置IOS欢迎界面的方法
    Java中的Nested Classes和Inner Classes
    pve之daemon
    对话沈向洋 | 在人工智能最好的时代,寻求可能性比超前顾虑更关键
    .NET Core 2.0 开源Office组件 NPOI
    【转发活动】Hey, 是你吗? | 寻"粉"启示
  • 原文地址:https://www.cnblogs.com/hoskey/p/4379029.html
Copyright © 2011-2022 走看看