【gdoi2018 day1】小学生图论题

题目大意:

lj竞赛图。

题解:

考虑把强联通分量缩点,大概是这样的(画图水平有限):
这里写图片描述

看那些红色标记的边,强联通分量个数=红色边的个数+1

红色边的判定条件?

设它左边的点集为S,右边的为T。

S到T的边方向一定要是S->T。

当m=0时,枚举S的大小,就可以算答案了。

Ans=n1i=1Cin2i(ni)

当m>0时,考虑用背包来解决这个问题。

对于一条路径,考虑枚举的S集它从左往右数的多少个点?

如果有i个,
i=0或i=n,系数的贡献是1。

i>0且i < n,因为 2i(ni) 这条式子包含了中间的这条连接边,而连接边的方向是确定的,所以系数要是2,去消掉一个多余的1/2。

设路径点数为k,看作一个多项式,大概是 (1+2x1+2x2++2xk1+xk)

其它的点的多项式是 (1+x)

用分治fft去加速这个把它们乘起来的过程,设最后的式子是A,则 Ans=n1i=1Ai2i(ni)

O(n log2 n) 居然能过,有点假。

其实上面本质是一个背包问题。

dalao yww 把多项式用生成函数乱搞,然后求exp,再回来,完全不懂,博主表示自己弱爆了~~

#include
#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define ff(i, x, y) for(int i = x; i < y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int mo = 998244353;

const int N = 1e5 + 5;

int n, m, l[N], r[N], x, p;

ll a[N * 2];

ll ksm(ll x, ll y) {
    ll s = 1;
    for(; y; y /= 2, x = x * x % mo)
        if(y & 1) s = s * x % mo;
    return s;
}

ll w[N * 4];
int tp;

void dft(ll *a, int n) {
    ff(i, 0, n) {
        int p = i, q = 0;
        fo(j, 1, tp) q = q * 2 + p % 2, p /= 2;
        if(q > i) swap(a[q], a[i]);
    }
    for(int m = 2; m <= n; m <<= 1) {
        int h = m / 2;
        ff(i, 0, h) {
            ll W = w[i * (n / m)];
            for(int j = i; j < n; j += m) {
                int k = j + h;
                ll u = a[j], v = a[k] * W % mo;
                a[j] = (u + v) % mo; a[k] = (u - v + mo) % mo;
            }
        }
    }
}
void fft(ll *a, ll *b, int n) {
    ll v = ksm(3, (mo - 1) / n); w[0] = 1;
    fo(i, 1, n) w[i] = w[i - 1] * v % mo;
    dft(a, n); dft(b, n); ff(i, 0, n) a[i] = a[i] * b[i] % mo;
    fo(i, 0, n / 2) swap(w[i], w[n - i]);
    dft(a, n); v = ksm(n, mo - 2);
    ff(i, 0, n) a[i] = a[i] * v % mo;
}

ll b[N * 4], c[N * 4];

void dg(int x, int y) {
    if(x == y) return;
    int m = x + y >> 1;
    dg(x, m); dg(m + 1, y);
    int n = r[x] - l[x] + r[m + 1] - l[m + 1];
    tp = 0; while(1 << ++ tp <= n);
    ff(i, 0, 1 << tp) b[i] = c[i] = 0;
    fo(i, l[x], r[x]) b[i - l[x]] = a[i];
    fo(i, l[m + 1], r[m + 1]) c[i - l[m + 1]] = a[i];
    fft(b, c, 1 << tp);
    r[x] = l[x] + n;
    fo(i, l[x], r[x]) a[i] = b[i - l[x]];
}

int main() {
    freopen("graph.in", "r", stdin);
    freopen("graph.out", "w", stdout);
    scanf("%d %d", &n, &m);
    r[0] = -1;
    fo(i, 1, m) {
        scanf("%d", &x);

        l[i] = r[i - 1] + 1;
        r[i] = l[i] + x;
        p += x;

        a[l[i]] = 1;
        fo(j, l[i] + 1, r[i] - 1) a[j] = 2;
        a[r[i]] = 1;

        int xx = x;
        fo(j, 1, xx) scanf("%d", &x);
    }
    fo(i, 1, n - p) ++ m, l[m] = r[m - 1] + 1, r[m] = l[m] + 1, a[l[m]] = a[r[m]] = 1;
    dg(1, m);
    ll ans = 0, v = ksm(2, mo - 2);
    fo(i, 1, n - 1) ans = (ans + a[i] * ksm(v, (ll) i * (n - i))) % mo;
    printf("%lld", (ans + 1) % mo);
}

你可能感兴趣的:(动态规划,数论杂集,FFT,NTT,FWT……,生成函数)