hdu5322 三次多校1007

来源:互联网 发布:东北大学网络教育官网 编辑:程序博客网 时间:2024/05/21 06:57

题目描述:

10000组测试数据,n(1≤n≤100000). 意思是n的所有全排列. 对于每一种全排列的价值这样定义:For each Ai, if there exists a minimum j satisfies j>i and Aj>Ai , then connect an edge between Ai and Aj. 然后看有几个联通块,数一数每个联通块内部的点的个数.然后把这些个数乘起来得到p.那么p^2就是这个排列的价值. 问这么多全排列的价值的和.

题解:

首先是分析题意,这样要直接考虑给定一个i,所有排列i对答案的贡献度.而不是一种一种排列去算. 算dp[n], 要缩小规模.发现n其实很特殊.n放在i的话,那么1~i的一定是一个联通块(这个是一个重点,i包括i以前的是一个特殊情况).i之后的n-i是一个子问题, 可以表示为dp[n-i],然后加上组合数,加上阶乘,能够写出来式子. dp[n] = sum(dp[n-j]/(n-j)! * j^j)(j从1到n). 剩下是两种方法快速求dp.首先(n-j )!可以无视.带上dp整体设为f[n-j].
法一:观察f[n-j]的系数:1 4 9 16…. 差分 3 5 7… 再差分 2 2 2 2….仔细一点维护3个sum值就可以o(1)的dp.
法二:将f[n-j]看做一部分, j^j 看做一部分, 发现对于n的贡献可以看做是一个卷积,次数和为n的j和n-j的权重系数的相乘. 但是我们并不知道前一部分的dp值. 这里就用到了cdq分治. dp1 到 n .那么取一个mid,先递归求1到mid, 然后考虑1到mid 对mid+1到n部分的影响.之后在影响的基础上再做mid+1到n的递归. 影响怎么算? 就可以用到卷积的和为n的性质. 构造用fft. 两边系数分别是f[i]和j*j. 然后乘起来, 把结果中次数为i的对应到mid+1的后面. 次数对应是几以及构造fft式子时候的细节认真处理下.另外要用到ntt. 对于这个mod,我们可以用他的原根3来搞.

重点:

1.考虑对答案的直接贡献.
2.dp缩小规模.并且全排列想到n的位置,特殊处理.
3.n放在i位,那么i和i之前的是一个已经定了的特殊情况,不是子问题. i之后的才是一个独立的子问题.
3.推导公式中有阶乘有组合数.把组合数拆掉和阶乘搞到一起.可以化简.
4.最后的递推式两种加速方式.

代码:

//o(1)递推版本.#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <cmath>#include <ctype.h>#include <limits.h>#include <cstdlib>#include <algorithm>#include <vector>#include <queue>#include <map>#include <stack>#include <set>#include <bitset>#define CLR(a) memset(a, 0, sizeof(a))#define REP(i, a, b) for(ll i = a;i < b;i++)#define REP_D(i, a, b) for(ll i = a;i <= b;i++)typedef long long ll;using namespace std;const ll maxn = 100000 + 100;const ll MOD = 998244353;ll dp[maxn], f[maxn];ll n;ll jc[maxn], jcrev[maxn];ll pow_mod(ll x,ll n){    ll res = 1;    ll t = x%MOD;    while(n)    {        if(n&1)            res = (res*t)%MOD;        t = (t*t)%MOD;        n >>= 1;    }    return res;}void getJC(){    jc[0] = 1;    jcrev[0] = 1;    for(ll i = 1;i<=100000;i++)    {        jc[i] = (jc[i-1]*i)%MOD;        jcrev[i] = (jcrev[i-1]*pow_mod(i, MOD-2))%MOD;//        if(i <= 4)//        {//            printf("i is %I64d    jc  %I64d  jcrev %I64d\n", i, jc[i], jcrev[i]);//        }    }}void getDp(){    dp[0] = 1;    f[0] = 1;    ll sum, sum_1, sum_1_1;    sum = 0;    sum_1 = 0;    sum_1_1 = 0;    REP_D(i, 1, 100000)    {        if(i == 1)        {            sum_1_1 = f[0];        }        else        {            sum_1_1 = (sum_1_1 + f[i-1])%MOD;            sum_1_1 = (sum_1_1 + f[i-2])%MOD;        }        sum_1 = (sum_1 + sum_1_1)%MOD;        sum = (sum + sum_1)%MOD;        dp[i] = (sum*jc[i-1])%MOD;        f[i] = (dp[i]*jcrev[i])%MOD;    }}int main(){    //freopen("7Gin.txt", "r", stdin);    //freopen("7Gout.txt", "w", stdout);    getJC();    getDp();    while(scanf("%I64d", &n) != EOF)    {        printf("%I64d\n", dp[n]);    }    return 0;}//ntt转移卷积加速.#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <cmath>#include <ctype.h>#include <limits.h>#include <cstdlib>#include <algorithm>#include <vector>#include <queue>#include <map>#include <stack>#include <set>#include <bitset>#define CLR(a) memset(a, 0, sizeof(a))#define REP(i, a, b) for(ll i = a;i < b;i++)typedef long long ll;using namespace std;const ll M = 998244353;const ll maxn = 5e5 + 100;ll dp[maxn];ll jc[maxn], jcrev[maxn];ll x1[maxn],x2[maxn];/** 进行FFT和IFFT前的反转变换。* 位置i和 (i二进制反转后位置)互换* len必须去2的幂*/void change(ll y[],ll len){    ll i,j,k;    for(i = 1, j = len/2; i <len-1; i++)    {        if(i < j)swap(y[i],y[j]);//交换互为小标反转的元素,i<j保证交换一次//i做正常的+1,j左反转类型的+1,始终保持i和j是反转的        k = len/2;        while(j >= k)        {            j -= k;            k /= 2;        }        if(j < k)j += k;    }}/** 做FFT* len必须为2^k形式,* on==1时是DFT,on==-1时是IDFT*/ll power(ll a, ll n){    ll res = 1, z = a;    while (n)    {        if (n & 1) res = res * z % M;        z = z * z % M;        n >>= 1;    }    return res;}ll inverse(ll x){    return power(x, M - 2);}void getJc(){    jc[0] = 1;    jcrev[0] = inverse(1);    for(ll i = 1;i <= 100000;i++)    {        jc[i] = jc[i-1]*i%M;        jcrev[i] = inverse(jc[i]);//        if(i < 10)//        {//            printf("i is %I64d  jcrev is %I64d\n", i, jcrev[i]);//        }    }}void fft(ll y[],ll len,ll on)//from 0 to len - 1;{    change(y,len);    for(ll h = 2; h <= len; h <<= 1)    {        ll unit_p0 = power(3, (M-1)/(h));//这个地方是重点        if(on == -1)        {            unit_p0 = inverse(unit_p0);        }        //Complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));        for(ll j = 0; j < len; j+=h)        {            ll unit = 1;            for(ll k = j; k < j+h/2; k++)            {                ll u = y[k];                ll t = unit*y[k+h/2]%M;                //Complex u = y[k];                //Complex t = w*y[k+h/2];                y[k] = u+t;                if(y[k] >= M)                {                    y[k] -= M;                }                if(u < t)                {                    u += M;                }                y[k+h/2] = u-t;                //y[k+h/2] = (u-t)%M + ;                // = w*wn;                unit = unit*unit_p0%M;            }        }    }    if(on == -1)    {        ll key = inverse(len);        for(ll i = 0; i < len; i++)            y[i] = y[i]*key%M;    }}void divid(ll l, ll r){    if(l==r)    {        return ;    }    ll mid = (l+r)/2;    divid(l, mid);    ll len = r-l+1;    ll len1 = 1;    while(len1<2*len)    {        len1 <<= 1;    }    for(ll i = 0;i<len1;i++)    {        x1[i] = 0;        x2[i] = 0;        if(i+l <= mid)        {            x1[i] = dp[i+l]*jcrev[i+l]%M;        }        if(i<=len)        {            x2[i] = i*i%M;        }    }    fft(x1, len1, 1);    fft(x2, len1, 1);    for(ll i = 0;i<len1;i++)    {        x1[i] = (x1[i]*x2[i])%M;    }    fft(x1, len1, -1);    for(ll i = mid+1;i<=r;i++)    {        dp[i] = (dp[i] + jc[i-1]*x1[i-l]%M)%M;    }    divid(mid+1, r);}ll n;int main(){   // freopen("12Lin.txt", "r", stdin);    //freopen("1out.txt", "w", stdout);    getJc();    CLR(dp);    dp[0] = 1;    divid(0, 100000);//    for(int i = 1;i<=20;i++)//    {//        printf("%d ", dp[i]);//    }    //printf("\n");    while(scanf("%I64d", &n) != EOF)    {        printf("%I64d\n", dp[n]);    }    return 0;}
0 0
原创粉丝点击