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;}
- hdu5322 三次多校1007
- HDU5322 Hope
- 三次作业
- 三次握手
- 三次握手
- 三次样条插值
- 三次握手
- 三次样条插值
- ”三次握手“
- 三次握手
- 三次样条插值
- 三次握手
- 三次样条插值
- 三次握手
- 三次握手
- 三次握手
- 三次样条插值
- 三次握手
- 扫描word文档提取email地址
- 二维数组输出空心菱形
- Netty实例2——外加聊天实力
- Unity第一人称和第三人称视角脚本
- 还是畅通工程
- hdu5322 三次多校1007
- 程序员,为什么千万不要重写代码?
- 2014年上海赛区现场赛 I 题 Defeat the Enemy
- 模仿微信声音锁的实现(运用DTW算法)
- zigbee矩阵算法
- easyui 获取指定列的值,格式化该列的值的实例
- 扫描word文档提取email地址
- Mysql海量数据存储和 分布式DB方案
- Algorithms—206.Reverse Linked List