2021HDU多校8 - 7057 Buying Snacks(矩阵快速幂套NTT优化dp)
題目鏈接:點擊查看
題目大意:給出 nnn 種糖果,每種糖果有大小包裝之分,有三種購買方案,價錢分別如下:
問給出的錢在范圍 [0,m][0,m][0,m] 內的不同購買方案數分別是多少
題目分析:
讀完題不難想到最簡單的 n2n^2n2 dp,dp[i][j]dp[i][j]dp[i][j] 代表前 iii 個糖果花費了 jjj 塊錢的方案數:dpi,j=dpi?1,j+dpi?1,j?1+dpi?1,j?2+dpi?2,j?1++2dpi?2,j?2+dpi?2,j?3dp_{i,j}=dp_{i-1,j}+dp_{i-1,j-1}+dp_{i-1,j-2}+\\dp_{i-2,j-1}++2dp_{i-2,j-2}+dp_{i-2,j-3}dpi,j?=dpi?1,j?+dpi?1,j?1?+dpi?1,j?2?+dpi?2,j?1?++2dpi?2,j?2?+dpi?2,j?3?
nnn 特別大, 所以考慮矩陣快速冪優化轉移。但是眾所周知,矩陣快速冪只能優化線性的 dpdpdp,也就是一維的 dpdpdp,所以我們需要用生成函數將上面的 dpdpdp 方程降維,因為是需要求方案數,所以也算是比較經典的模型了
Fi=(1+x+x2)Fi?1+(x+2x2+x3)Fi?2F_i=(1+x+x^2)F_{i-1}+(x+2x^2+x^3)F_{i-2}Fi?=(1+x+x2)Fi?1?+(x+2x2+x3)Fi?2?
所以將矩陣快速冪中的矩陣用多項式來表示就可以了,不過如果只是單純的套上板子會超時,因為一次矩陣乘法需要做 2?2?2?3=242*2*2*3=242?2?2?3=24 次 NTTNTTNTT,常數巨大,但是不難發現,在矩陣相乘的過程中只涉及到了多項式的乘法和加法,這個用點值表示和用插值表示都是無所謂的,所以我們可以在一開始就將需要相乘的兩個矩陣用做 2?2?2=82*2*2=82?2?2=8 次 NTTNTTNTT 轉換為點值,中間運算的過程保持點值,最后再將答案矩陣做 2?22*22?2 次 NTTNTTNTT 轉換為插值返回,這樣就能少掉一半的常數,就可以通過本題了
代碼:
// Problem: Buying Snacks // Contest: Virtual Judge - HDU // URL: https://vjudge.net/problem/HDU-7057 // Memory Limit: 262 MB // Time Limit: 3000 ms // // Powered by CP Editor (https://cpeditor.org)// #pragma GCC optimize(2) // #pragma GCC optimize("Ofast","inline","-ffast-math") // #pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> #include<list> #include<unordered_map> #define lowbit(x) (x&-x) using namespace std; typedef long long LL; typedef unsigned long long ull; template<typename T> inline void read(T &x) {T f=1;x=0;char ch=getchar();while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();x*=f; } template<typename T> inline void write(T x) {if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0'); } const int inf=0x3f3f3f3f; const int N=1e6+100; const int mod=998244353,G=3,Gi=332748118; typedef vector<int> Poly; int limit,L,r[N],UP; int tmpa[2][2][N],tmpb[2][2][N],tmpc[2][2][N]; int q_pow(int a,int b) {int ans=1;while(b) {if(b&1) ans=1LL*ans*a%mod;a=1LL*a*a%mod,b>>=1;}return ans; } void NTT(int *A,int type) {for(int i=0;i<limit;i++) if(i<r[i]) swap(A[i],A[r[i]]);for(int mid=1;mid<limit;mid<<=1) { int Wn=q_pow(type==1?G:Gi,(mod-1)/(mid<<1));for(int j=0;j<limit;j+=(mid<<1)) {int w=1;for(int k=0;k<mid;k++,w=1LL*w*Wn%mod) {int x=A[j+k],y=1LL*w*A[j+k+mid]%mod;A[j+k]=(x+y)%mod,A[j+k+mid]=(x-y+mod)%mod;}}}if(type==-1) {int inv=q_pow(limit,mod-2);for(int i=0;i<limit;i++) {A[i]=1LL*A[i]*inv%mod;}} } void init(int n,int m) {limit=1;L=0;while(limit<=n+m) limit<<=1,L++;for(int i=0;i<limit;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(L-1)); } struct Matrix {Poly a[2][2];Poly*operator[](int x) {return a[x];}const Poly*operator[](int x) const {return a[x];}Matrix() {for(int i=0;i<2;i++) {for(int j=0;j<2;j++) {a[i][j]=Poly{};}}}friend Matrix operator * (const Matrix& A,const Matrix& B)//矩陣乘法{Matrix ans;int n=0,m=0;for(int i=0;i<2;i++) for(int j=0;j<2;j++) n=max(n,(int)A[i][j].size());for(int i=0;i<2;i++) for(int j=0;j<2;j++) m=max(m,(int)B[i][j].size());init(n+1,m+1);for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<limit;k++) tmpa[i][j][k]=k<(int)A[i][j].size()?A[i][j][k]:0;for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<limit;k++) tmpb[i][j][k]=k<(int)B[i][j].size()?B[i][j][k]:0;for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<limit;k++) tmpc[i][j][k]=0;for(int i=0;i<2;i++) for(int j=0;j<2;j++) NTT(tmpa[i][j],1),NTT(tmpb[i][j],1);for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) for(int p=0;p<limit;p++) {tmpc[i][j][p]=(tmpc[i][j][p]+1LL*tmpa[i][k][p]*tmpb[k][j][p])%mod;}for(int i=0;i<2;i++) for(int j=0;j<2;j++) NTT(tmpc[i][j],-1);if(limit>UP) {limit=UP;}for(int i=0;i<2;i++) for(int j=0;j<2;j++) {ans[i][j].resize(limit);for(int k=0;k<limit;k++) {ans[i][j][k]=tmpc[i][j][k];}}return ans;}Matrix operator^(int b)//矩陣A的b次方{Matrix ret;for(int i=0;i<2;i++) for(int j=0;j<2;j++) ret[i][j]=i==j?Poly{1}:Poly{}; Matrix A=*this;while(b) { if(b&1) ret=ret*A; A=A*A;b>>=1;}return ret;} }st,tr; int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);st[0][0]=Poly{1,1,1};st[0][1]=Poly{1};tr[0][0]=Poly{1,1,1};tr[0][1]=Poly{1};tr[1][0]=Poly{0,1,2,1};int w;cin>>w;while(w--) {int n,m;read(n),read(m);UP=m+1;Poly ans=(st*(tr^(n-1))).a[0][0];for(int i=1;i<=m;i++) {printf("%d ",ans[i]);}puts("");}return 0; }總結
以上是生活随笔為你收集整理的2021HDU多校8 - 7057 Buying Snacks(矩阵快速幂套NTT优化dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021牛客多校6 - Gambling
- 下一篇: 2021牛客多校10 - Browser