HDU - 5322 Hope

题目大意

求$$f_i=(i-1)!\sum_{j=0}^{i-1}(i-j)^2{f_j\over j!}$$

简要题解

为求$f_i$我们需要知道$f_0,\cdots,f_{i-1}$,考虑cdq分治,把卷积拆开成关于已知的$f_i$和还没计算出来的部分,发现已知部分还是卷积形式,求出来累加上去就好。

此外还写了个求原根,暴力枚举原根$a$,然后判断是否只有$a^{p-1}=1(\mod p)$而$a^{p-1\over p_i}=1(\mod p)$皆不成立,其中$p_i$是$p$的所有素因子。

#include <bits/stdc++.h>
using namespace std;
namespace my_header {
#define pb push_back
#define mp make_pair
#define pii pair<int, int>
#define vec vector<int>
#define pc putchar
#define clr(t) memset(t, 0, sizeof t)
#define pse(t, v) memset(t, v, sizeof t)
#define bl puts("")
#define wn(x) wr(x), bl
#define ws(x) wr(x), pc(‘ ‘)
    const int INF = 0x3f3f3f3f;
    typedef long long LL;
    typedef double DB;
    inline char gchar() {
        char ret = getchar();
        for(; (ret == ‘\n‘ || ret == ‘\r‘ || ret == ‘ ‘) && ret != EOF; ret = getchar());
        return ret; }
    template<class T> inline void fr(T &ret, char c = ‘ ‘, int flg = 1) {
        for(c = getchar(); (c < ‘0‘ || ‘9‘ < c) && c != ‘-‘; c = getchar());
        if (c == ‘-‘) { flg = -1; c = getchar(); }
        for(ret = 0; ‘0‘ <= c && c <= ‘9‘; c = getchar())
            ret = ret * 10 + c - ‘0‘;
        ret = ret * flg; }
    inline int fr() { int t; fr(t); return t; }
    template<class T> inline void fr(T&a, T&b) { fr(a), fr(b); }
    template<class T> inline void fr(T&a, T&b, T&c) { fr(a), fr(b), fr(c); }
    template<class T> inline char wr(T a, int b = 10, bool p = 1) {
        return a < 0 ? pc(‘-‘), wr(-a, b, 0) : (a == 0 ? (p ? pc(‘0‘) : p) :
            (wr(a/b, b, 0), pc(‘0‘ + a % b)));
    }
    template<class T> inline void wt(T a) { wn(a); }
    template<class T> inline void wt(T a, T b) { ws(a), wn(b); }
    template<class T> inline void wt(T a, T b, T c) { ws(a), ws(b), wn(c); }
    template<class T> inline void wt(T a, T b, T c, T d) { ws(a), ws(b), ws(c), wn(d); }
    template<class T> inline T gcd(T a, T b) {
        return b == 0 ? a : gcd(b, a % b); }
    template<class T> inline T fpw(T b, T i, T _m, T r = 1) {
        for(; i; i >>= 1, b = b * b % _m)
            if(i & 1) r = r * b % _m;
        return r; }
};
using namespace my_header;

const int MAXN = 2e6 + 100;
namespace NTT {

const int MOD = 998244353;

int inv[MAXN];

vec getPrime(vec&isPrime, int rng = 1000000) {
    isPrime.resize(rng);
    fill(isPrime.begin(), isPrime.end(), 1);
    vec pri;
    isPrime[0] = isPrime[1] = 0;
    inv[1] = 1;
    for (int i = 2; i < rng; ++i) {
        if (isPrime[i]) {
            pri.pb(i);
            inv[i] = fpw((LL)i, MOD-2LL, (LL)MOD);
        }
        for (int j = 0; j < (int)pri.size() && pri[j] * 1LL * i < rng; ++j) {
            isPrime[i * 1LL * pri[j]] = 0;
            inv[i * 1LL * pri[j]] = 1LL * inv[i] * inv[pri[j]] % MOD;
        }
    }
    return pri;
}

void dfs_enum(vector<pii>&fac, int p, int d, vec&res) {
    if (d == (int)fac.size()) {
        res.pb(p);
        return ;
    }
    int v = fac[d].first, t = fac[d].second, c = 1;
    dfs_enum(fac, p, d + 1, res);
    for (int i = 0; i < t; ++i) {
        c = c * v;
        dfs_enum(fac, p * c, d + 1, res);
    }
}

vec enumerate(vector<pii>&fac) {
    vec res;
    dfs_enum(fac, 1, 0, res);
    return res;
}

vec factorize(LL val) {
    vec isPrime;
    vector<pii> fac;
    vec pri = getPrime(isPrime);
    for (int i = 0; i < (int)pri.size(); ++i) {
        int p = pri[i];
        if (val % p == 0) {
            fac.pb(mp(p, 0));
            while (val % p == 0) {
                val /= p;
                ++fac.back().second;
            }
        }
    }
    if (val != 1)
        fac.pb(mp(val, 1));
    return enumerate(fac);
}

int getPrimeRoot(LL val) {
    int pr;
    vec f = factorize(val - 1);
    for (pr = 2; ; ++pr) {
        if (fpw((LL)pr, val - 1LL, (LL)MOD) == 1) {
            bool ok = true;
            for (int i = 0; i < (int)f.size(); ++i)
                if (f[i] != (val - 1) && fpw((LL)pr, (LL)f[i], (LL)MOD) == 1)
                    ok = false;
            if (ok) return pr;
        }
    }
}

int GN[24], rGN[24];
int getGN(int m, int r) {
    if (r == 1) {
        if (GN[m] != 0) return GN[m];
        return GN[m] = fpw(3LL, (MOD-1LL) / (1<<m), (LL)MOD);
    } else {
        if (rGN[m] != 0) return rGN[m];
        return rGN[m] = fpw((LL)GN[m], MOD-2LL, (LL)MOD);
    }
}

int b[MAXN];

void dft(int *a, int m, int r) {
    if (m == 0) return ;
    int N = 1<<m;
    memcpy(b, a, (sizeof a[0]) * N);
    for (int i = 0; i < (N>>1); ++i)
        a[i] = b[i<<1], a[(N>>1) + i] = b[(i<<1)|1];
    dft(a, m - 1, r), dft(a + (N>>1), m - 1, r);
    int gn = getGN(m, r), c = 1;
    for (int i = 0; i < (N>>1); c = 1LL * c * gn % MOD, ++i) {
        b[i] = (MOD + (a[i] + 1LL * c * a[i + (N>>1)] % MOD) % MOD) % MOD;
        b[i + (N>>1)] = (MOD + (a[i] - 1LL * c * a[i + (N>>1)] % MOD) % MOD) % MOD;
    }
    memcpy(a, b, (sizeof a[0]) * N);
}

};
using namespace NTT;

int j2[MAXN], fac[MAXN], ifac[MAXN], ta[MAXN], tb[MAXN], f[MAXN];

void calc(int l, int r) {
    if (l == r) return;
    int m = (l + r) >> 1;
    calc(l, m);
    int len = r - l + 1, t = 0;
    for (; (1<<t) < len; ++t);
    for (int i = l; i <= m; ++i)
        ta[i - l] = f[i] * 1LL * ifac[i] % MOD;
    for (int i = m + 1; i < m + (1<<t); ++i)
        ta[i - l] = 0;
    for (int i = 0; i < (1<<t); ++i)
        tb[i] = j2[i] % MOD;
    dft(ta, t, 1);
    dft(tb, t, 1);
    for (int i = 0; i < (1<<t); ++i)
        ta[i] = ta[i] * 1LL * tb[i] % MOD;
    dft(ta, t, -1);
    for (int i = m + 1; i <= r; ++i)
        (f[i] += 1LL * fac[i - 1] * ta[i - l] % MOD * inv[1<<t] % MOD) %= MOD;
    calc(m + 1, r);
}

int main() {
#ifdef lol
    freopen("G.in", "r", stdin);
    freopen("G.out", "w", stdout);
#endif

    getPrimeRoot(MOD);

    fac[0] = 1;
    j2[0] = 0;
    for (int i = 1; i < MAXN; ++i) {
        fac[i] = fac[i-1] * 1LL * i % MOD;
        j2[i] = i * 1LL * i % MOD;
    }
    ifac[MAXN-1] = fpw((LL)fac[MAXN-1], MOD - 2LL, (LL)MOD);
    for (int i = MAXN-2; 0 <= i; --i)
        ifac[i] = (i + 1LL) * ifac[i + 1] % MOD;

    f[0] = 1;
    calc(0, 100000);
    int x;
    while (scanf("%d", &x) != EOF)
        wt(f[x]);

    return 0;
}
时间: 2024-10-24 08:22:05

HDU - 5322 Hope的相关文章

HDU 5322 Hope (分治NTT优化DP)

题面传送门 题目大意: 假设现在有一个排列,每个数和在它右面第一个比它大的数连一条无向边,会形成很多联通块. 定义一个联通块的权值为:联通块内元素数量的平方. 定义一个排列的权值为:每个联通块的权值之积 求长度为$n$所有排列的权值之和,$n\leq 1e5$,$1e4$组询问 原题面描述不清楚啊..害得我白想了30min 和ZOJ3874一样都是排列$DP$问题 $DP$方程还是不难想的 假设现在有一个$i-1$的排列,当我们把$i$某个位置上时 $i$前面的数都会和$i$连通,$i$后面的数

HDU 5319 Painter(枚举)

Painter Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 745    Accepted Submission(s): 345 Problem Description Mr. Hdu is an painter, as we all know, painters need ideas to innovate , one day,

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

hdu 1207 汉诺塔II (DP+递推)

汉诺塔II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4529    Accepted Submission(s): 2231 Problem Description 经典的汉诺塔问题经常作为一个递归的经典例题存在.可能有人并不知道汉诺塔问题的典故.汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往

[hdu 2102]bfs+注意INF

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2102 感觉这个题非常水,结果一直WA,最后发现居然是0x3f3f3f3f不够大导致的--把INF改成INF+INF就过了. #include<bits/stdc++.h> using namespace std; bool vis[2][15][15]; char s[2][15][15]; const int INF=0x3f3f3f3f; const int fx[]={0,0,1,-1};

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

HDU 5917 Instability ramsey定理

http://acm.hdu.edu.cn/showproblem.php?pid=5917 即世界上任意6个人中,总有3个人相互认识,或互相皆不认识. 所以子集 >= 6的一定是合法的. 然后总的子集数目是2^n,减去不合法的,暴力枚举即可. 选了1个肯定不合法,2个也是,3个的话C(n, 3)枚举判断,C(n, 4), C(n, 5) #include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using name

hdu 6166 Senior Pan

地址:http://acm.split.hdu.edu.cn/showproblem.php?pid=6166 题目: Senior Pan Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 245    Accepted Submission(s): 71 Problem Description Senior Pan fails i