1. 程式人生 > 實用技巧 >NOI2020D1T1美食家

NOI2020D1T1美食家

傳送門:QAQQAQ

完了完了NOI簽到題全班打不出來,真就全部成為時代的眼淚了。。。

首先$O(mT)$的$dp$顯然,然後因為$T$很大$w$很小矩陣快速冪顯然,但是有$k=200$卡不過去。

然後因為行向量乘上轉移矩陣是$O(n^{2})$的,所以我們列舉的$k$時只用行向量乘上轉移矩陣,轉移矩陣的自乘放在外面倍增預處理,這樣複雜度是$O(n^{2}*k*log(V)+(n^{3}*log(V)))$,開了O2非常穩

所以最近兩道幾乎正解的矩乘都沒打出來。。一道倍增預處理轉移矩陣優化,一道先DFT轉點值再快速冪而不是每次快速冪用FFT優化來將複雜度。。

菜是原罪。

程式碼:

#include<bits/stdc++.h>
using
namespace std; typedef long long ll; typedef pair<int,int> pii; const ll inf=(ll)1e15; #define mk make_pair void checkmin(ll &x,ll y){if(x>y) x=y;} void checkmax(ll &x,ll y){if(x<y) x=y;} const int N=2010; vector<pii> v[N],G[N]; int t[N],X[N],Y[N],c[N]; int n,m,T,k; struct Edge {
int from,to,cost; }E[N]; void init() { scanf("%d%d%d%d",&n,&m,&T,&k); for(int i=1;i<=n;i++) scanf("%d",&c[i]); for(int i=1;i<=m;i++) { scanf("%d%d%d",&E[i].from,&E[i].to,&E[i].cost); v[E[i].from].push_back(mk(E[i].to,E[i].cost)); G[E[i].to].push_back(mk(E[i].
from,E[i].cost)); } for(int i=1;i<=k;i++) scanf("%d%d%d",&t[i],&X[i],&Y[i]); } namespace solver1{ ll dp[52600][55]; int a[52600][55]; void init() { memset(a,0,sizeof(a)); for(int i=1;i<=k;i++) a[t[i]][X[i]]=Y[i]; } void main() { init(); for(int i=0;i<=T;i++) for(int j=1;j<=n;j++) dp[i][j]=-inf; dp[0][1]=c[1]; for(int i=1;i<=T;i++) { for(int j=1;j<=n;j++) { for(int p=0;p<(int)G[j].size();p++) { int to=G[j][p].first,w=G[j][p].second; if(i<w) continue; checkmax(dp[i][j],dp[i-w][to]+c[j]); } } for(int j=1;j<=n;j++) dp[i][j]+=a[i][j]; } if(dp[T][1]<=0) puts("-1"); else printf("%lld\n",dp[T][1]); } } struct matrix{ ll a[255][255]; int n,m; matrix(){} matrix(int n,int m):n(n),m(m){ for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=-inf; } }; matrix operator * (matrix A,matrix B) { matrix C(A.n,B.m); for(int i=1;i<=C.n;i++) { for(int k=1;k<=A.m;k++) { if(A.a[i][k]==-inf) continue; for(int j=1;j<=C.m;j++) checkmax(C.a[i][j],A.a[i][k]+B.a[k][j]); } } return C; } struct LI{ int t,x,y; LI(){} LI(int t,int x,int y):t(t),x(x),y(y){} bool operator < (const LI &rhs) const{ return t<rhs.t; } }limit[N]; matrix Base[31]; void Qpow(matrix &A,int y) { matrix ret(A.n,A.m); for(int i=1;i<=A.n;i++) ret.a[i][i]=0; for(int i=30;i>=0;i--) { if((1<<i)<=y) y-=(1<<i),A=A*Base[i]; } } namespace solver2{ ll dp1[7][55]; int a[7][55]; void init() { memset(a,0,sizeof(a)); for(int i=1;i<=k;i++) if(t[i]<=5) a[t[i]][X[i]]=Y[i]; } void build() { init(); for(int i=0;i<=5;i++) for(int j=1;j<=n;j++) dp1[i][j]=-inf; dp1[0][1]=c[1]; for(int i=1;i<=5;i++) { for(int j=1;j<=m;j++) { int u=E[j].from,to=E[j].to,w=E[j].cost; if(i<E[j].cost) continue; if(dp1[i-w][u]==-inf) continue; checkmax(dp1[i][to],dp1[i-w][u]+c[to]); } for(int j=1;j<=n;j++) dp1[i][j]+=a[i][j]; } } void main() { build(); int TMP=0; for(int i=1;i<=k;i++) { if(t[i]<=5) i--,k--,TMP++; limit[i]=LI(t[i+TMP],X[i+TMP],Y[i+TMP]); } sort(limit+1,limit+k+1); matrix A(1,n*5); for(int i=1;i<=5;i++) { for(int j=1;j<=n;j++) A.a[1][(i-1)*n+j]=dp1[i][j]; } //print(A); for(int i=0;i<=30;i++) { Base[i].m=Base[i].n=5*n; for(int j=1;j<=5*n;j++) { for(int t=1;t<=5*n;t++) { Base[i].a[j][t]=-inf; } } } for(int j=1;j<=n*4;j++) Base[0].a[j+n][j]=0; for(int i=1;i<=m;i++) { int u=E[i].from,to=E[i].to,w=E[i].cost; int from=(5-w)*n+u; to=4*n+to; Base[0].a[from][to]=c[E[i].to]; } //print(Base); for(int i=1;i<=30;i++) Base[i]=Base[i-1]*Base[i-1]; T-=5; for(int i=1;i<=k;i++) limit[i].t-=5; int now=0; for(int i=1;i<=k;i++) { int pos=limit[i].t; Qpow(A,pos-now); now=pos; if(A.a[1][4*n+limit[i].x]==-inf) continue; else A.a[1][4*n+limit[i].x]+=limit[i].y; } Qpow(A,T-now); if(A.a[1][4*n+1]<=0) puts("-1"); else printf("%lld\n",A.a[1][4*n+1]); } } namespace solver3{ int dis[N],vis[N]; ll tot=0; void dfs(int u){ if(vis[u]) return; vis[u]=1; for(int i=0;i<(int)v[u].size();i++) { int to=v[u][i].first,w=v[u][i].second; dis[to]=dis[u]+w; dfs(to); } } void main() { memset(vis,0,sizeof(vis)); dis[1]=0; dfs(1); if(T%dis[1]!=0) { puts("-1"); return; } ll ans=c[1]; for(int i=1;i<=k;i++) { if((t[i]-dis[X[i]])%dis[1]==0) ans+=Y[i]; } for(int i=1;i<=n;i++) tot+=c[i]; ans+=T/dis[1]*tot; printf("%lld\n",ans); } } bool iscircle() { if(n!=m) return 0; for(int i=1;i<=m;i++) { if(E[i].to!=E[i].from%n+1) return 0; } return 1; } void solve() { if(T<=52600) { solver1::main(); return; } if(iscircle()) { solver3::main(); return; } solver2::main(); return; } int main() { //freopen("delicacy.in","r",stdin); //freopen("delicacy.out","w",stdout); init(); solve(); fclose(stdin); fclose(stdout); return 0; }
View Code