zoukankan      html  css  js  c++  java
  • HDU5322 Hope(DP + CDQ分治 + NTT)

    题目

    Source

    http://acm.hdu.edu.cn/showproblem.php?pid=5322

    Description

    Hope is a good thing, which can help you conquer obstacles in your life, just keep fighting, and solve the problem below.

    In mathematics, the notion of permutation relates to the act of arranging all the members of a set into some sequence or order, or if the set is already ordered, rearranging (reordering) its elements, a process called permuting. These differ from combinations, which are selections of some members of a set where order is disregarded. For example, written as tuples, there are six permutations of the set {1,2,3}, namely: (1,2,3), (1,3,2), (2,1,3), (2,3,1), (3,1,2), and (3,2,1). These are all the possible orderings of this three element set. As another example, an anagram of a word, all of whose letters are different, is a permutation of its letters. In this example, the letters are already ordered in the original word and the anagram is a reordering of the letters.
    There is a permutation A1,A2,...An, now we define its value as below:
    For each Ai, if there exists a minimum j satisfies j>i and Aj>Ai , then connect an edge between Ai and Aj , so after we connect all the edges, there is a graph G, calculate the product of the number of nodes in each component as an integer P. The permutation value is P * P.Now, Mr. Zstu wants to know the sum of all the permutation value of n. In case the answer is very big, please output the answer mod 998244353.
    Just in case some of you can’t understand, all the permutations of 3 are
    1 2 3
    1 3 2
    2 3 1
    2 1 3
    3 1 2
    3 2 1

    Input

    There are multiple test cases.
    There are no more than 10000 test cases.
    Each test case is an integer n(1≤n≤100000).

    Output

    For each test case, output the answer as described above.

    Sample Input

    1
    2

    Sample Output

    1
    5

    分析

    题目大概说,对于1到n这n个数的任何一个排列A可以这样计算其价值:对所有下标i找到最小的j满足j>i且A[j]>A[i],然后i和j之间连边,最后所有连通块个数之积的平方就是该排列的价值。问所有排列的价值和是多少。

    • 首先要得出这么一个结论:从下标1...x,如果下标x的数是最大的话,那么1...x-1就与x一起组成一个连通块了。
    • 然后,$dp[i]表示i个互不相同的数的所有排列的价值和$
    • 通过枚举最大数的位置j来转移:$dp[i] = sum A_{i-1}^{j-1}j^2dp[i-j]$
    • 可以整理成卷积形式:$dp[i] = (i-1)! imes sum (j^2 imes ((i-j)!)^{-1}dp[i-j])$
    • 然后于是就能用FFT计算了,特别的是结果模998244353,直接用NTT即可;还有要利用CDQ分治加速,累加左半边已经求得的dp值对右半边的影响。时间复杂度$O(nlog^2n)$。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define MAXN 262144
    
    const int P=998244353; // 119 * 2 ^ 23 + 1
    const int G=3;
     
    long long mul(long long x,long long y){
    	return (x*y-(long long)(x/(long double)P*y+1e-3)*P+P)%P;
    }
    long long qpow(long long x,long long k,long long p){
    	long long ret=1;
    	while(k){
    		if(k&1) ret=mul(ret,x);
    		k>>=1;
    		x=mul(x,x);
    	}
    	return ret;
    }
     
    long long wn[25];
    void getwn(){
    	for(int i=1; i<=21; ++i){
    		int t=1<<i;
    		wn[i]=qpow(G,(P-1)/t,P);
    	}
    }
     
    int len;
    void NTT(long long y[],int op){
    	for(int i=1,j=len>>1,k; i<len-1; ++i){
    		if(i<j) swap(y[i],y[j]);
    		k=len>>1;
    		while(j>=k){
    			j-=k;
    			k>>=1;
    		}
    		if(j<k) j+=k;
    	}
    	int id=0;
    	for(int h=2; h<=len; h<<=1) {
    		++id;
    		for(int i=0; i<len; i+=h){
    			long long w=1;
    			for(int j=i; j<i+(h>>1); ++j){
    				long long u=y[j],t=mul(y[j+h/2],w);
    				y[j]=u+t;
    				if(y[j]>=P) y[j]-=P;
    				y[j+h/2]=u-t+P;
    				if(y[j+h/2]>=P) y[j+h/2]-=P;
    				w=mul(w,wn[id]);
    			}
    		}
        }
        if(op==-1){
    		for(int i=1; i<len/2; ++i) swap(y[i],y[len-i]);
    		long long inv=qpow(len,P-2,P);
    		for(int i=0; i<len; ++i) y[i]=mul(y[i],inv);
        }
    }
    void Convolution(long long A[],long long B[],int n){
    	for(len=1; len<(n<<1); len<<=1);
    	for(int i=n; i<len; ++i){
    		A[i]=B[i]=0;
    	}
    	
    	NTT(A,1); NTT(B,1);
    	for(int i=0; i<len; ++i){
    		A[i]=mul(A[i],B[i]);
    	}
    	NTT(A,-1);
    }
    
    long long fact[MAXN]={1},fact_ine[MAXN]={1};
    
    long long A[MAXN],B[MAXN];
    long long d[MAXN]={1};
    
    /*
    	d[i] = fact[i-1] * Σj*j * fact_ine[i-j]*d[i-j]
    */
    void cdq(int l,int r){
    	if(l==r) return;
    	int mid=l+r>>1;
    	cdq(l,mid);
    	for(int i=l; i<=mid; ++i) A[i-l]=mul(fact_ine[i],d[i]);
    	for(int i=1; i<=r-l; ++i) B[i]=mul(i,i);
    	for(int i=mid-l+1; i<=r-l; ++i) A[i]=0;
    	Convolution(A,B,r-l+1);
    	for(int i=mid+1; i<=r; ++i){
    		d[i]+=mul(A[i-l],fact[i-1]);
    		d[i]%=P;
    	}
    	cdq(mid+1,r);
    }
    
    int main(){
    	for(int i=1; i<=100000; ++i){
    		fact[i]=mul(fact[i-1],i);
    		fact_ine[i]=qpow(fact[i],P-2,P);
    	}
    	getwn();
    	cdq(0,100000);
    	int n;
    	while(~scanf("%d",&n)){
    		printf("%I64d
    ",d[n]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    头插法链表的基本操作:创建空链表,插入结点,遍历链表,求链表长度,查找结点,删除结点
    尾插法链表拆分
    头插法链表拆分
    尾插法创建链表
    头插法创建链表
    二维数组45度反斜线扫描分析。
    [LeetCode] Binary Search Tree Iterator | 二叉查找树迭代器
    只用递归翻转栈
    [LeetCode] Wiggle Sort II | 摆动排序
    今天回归刷题的日子
  • 原文地址:https://www.cnblogs.com/WABoss/p/5917793.html
Copyright © 2011-2022 走看看