題面傳送門
題目大意:
假設現在有乙個排列,每個數和在它右面第乙個比它大的數連一條無向邊,會形成很多聯通塊。
定義乙個聯通塊的權值為:聯通塊內元素數量的平方。
定義乙個排列的權值為:每個聯通塊的權值之積
求長度為$n$所有排列的權值之和,$n\leq 1e5$,$1e4$組詢問
原題面描述不清楚啊..害得我白想了30min
和zoj3874一樣都是排列$dp$問題
$dp$方程還是不難想的
假設現在有乙個$i-1$的排列,當我們把$i$某個位置上時
$i$前面的數都會和$i$連通,$i$後面的數一定不和$i$前面的數連通
利用這個性質就可以$dp$了,定義$f[i]$表示長度為$i$的所有排列的權值之和
$f[i]$
$i$後面一共j個數可以在$i-1$個數中任選,$i$前面一共$i-j-1$個數可以任意排列,可得
$=\sum c_^f[j](i-j-1)!(i-j)^$
化簡$=(i-1)!\sum \frac(i-j)^$
發現這是乙個卷積的形式,用分治$ntt$解決即可
1 #include 2 #include 3 #include 4 #include 5#define n1 (1<<18)+10
6#define il inline
7#define dd double
8#define ld long double
9#define ll long long
10using
namespace
std;
1112
const
int inf=0x3f3f3f3f;13
const ll p=998244353;14
intgint()
1518
while(c>='
0'&&c<='9')
19return ret*fh;20}
21ll qpow(ll x,ll y)
2227
28namespace
ntt39
void ntt(ll *s,int len,int type,int
l)4054}
55}56}
57void main(int len,int
l)58
65void clr(int
sz)66
7071
};72
73using ntt::a; using ntt::b; using
ntt::c;
74 ll f[n1],g[n1],ans[n1],mul[n1],_mul[n1]; int
de;75
void cdq(int l,int
r)76
82if(r-l<=1) return;83
int mid=(l+r)>>1
,i,len,l;
84cdq(l,mid);
85for(len=1,l=0;len<(mid-l)+(r-l)-1;len<<=1,l++);
86for(i=l;if[i];
87for(i=0;i<(r-l);i++) ntt::b[i]=g[i];
88ntt::main(len,l);
89for(i=mid;ip;
90ntt::clr(len);
91cdq(mid,r);92}
93int
t,n,m;
94int
que[n1];
9596
intmain()
97
HDU 4812 D Tree 點分治 逆元
題目要求在樹上找到一條鏈使得這條鏈上的點的乘積模mod等於k,求鏈首尾字典序最小的一條 看到題目就能知道是乙個點分治的題目,將樹按照重心分治之後,就是要統計以重心為根的子樹中,過樹根的mod為k的鏈字典序最小的一條,這裡的統計必需是在時間複雜度o n 以下才能過 pragma comment lin...
HDU 5324(分治 樹狀陣列)
本題目的原來意思是,給定兩個長度為n的陣列,l,r,要求乙個子串行 可以不連續 使的l遞減,r遞增。分析 加上下標遞增,兩個維度增,乙個減,那麼考慮 d i 代表以i為起點的串往後找能得到的最大長度,用分治方法更新最有值。那麼下面說一下,怎麼分治維護最優。首先,陣列原順序保持不變,就是下標遞增,對於...
hdu(1007) 最近點對 分治法
最近點對一般想到列舉 一一枚舉時間複雜度為n 2 列舉時候一些操作是多餘的,有了分治演算法的思想 把一些問題分個擊破,再回到整體。題目鏈結 以這道題為例,我們可以把他按照x軸的公升序分成多個子區域先在子區域中求最近點距離,然後將相鄰兩個子區域合併,看看兩個子區域中有沒有更小的。大致思想就是這樣的。設...