NOIP2020 T4微信步數
阿新 • • 發佈:2020-12-17
題目描述
有一個\(k\)維空間,以及一個行走方案,求從空間中每個點出發按行走方案行走走出範圍的步數之和。
題解
這題考試時沒有細想,只打了暴力,時間都拿去肝T3了。
難度其實也跟平時模擬賽做的難題差不多,但的的確確是超出了自己的能力範圍。
首先考慮將步數之和轉化為在走了當前步數時,還有多少個點沒有走出範圍,然後以此統計答案。
容易發現,對於每一層而言,都是最兩側的點最先走出去。並且可以進一步發現,從第二輪開始,每輪走出去的點數都是相同的。
於是可以考慮先處理出第一輪的答案,然後計算出從第二輪開始每一輪走出去的點數。
於是不妨考慮先列舉當前是在某一輪的第\(i\)步,然後考慮當前這一步會在第幾輪讓所有的點都走出範圍。於是會發現對於後半部分會是一個多項式的部分,可以考慮設\(f_{i,j}\)
對於最後如何利用這個求答案的部分:發現當\(k\ge3\)時,輪數最多隻有\(1000000\),於是可以預處理;當\(k\le3\)時,可以直接用公式算。
參考了:https://www.luogu.com.cn/blog/Troverld/solution-p7116
程式碼
#include<cstdio> #include<cstring> #include<algorithm> #define N 500010 #define mo 1000000007 #define ll long long #define two 500000004 #define six 166666668 #define four 250000002 #pragma GCC optimize(2) #define K 11 using namespace std; ll n,k,ans,i,j,p,T,w[K],f[K][K],sum[N*2][K],mi[N*2][K],c,d,e[K],l[N][K],r[N][K],temp,a[K],b[K],bzans,mx; ll read(){ ll x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9'){ if (ch=='-') f=-1; ch=getchar(); } while (ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } ll solve(ll T,ll k){ if (k>3) return sum[T][k]; if (k==0) return T+1; if (k==1) return T*(T+1)%mo*two%mo; if (k==2) return T*(T+1)%mo*(2*T+1)%mo*six%mo; return T*T%mo*(T+1)%mo*(T+1)%mo*four%mo; } int main(){ freopen("walk.in","r",stdin); freopen("walk.out","w",stdout); n=read();k=read(); for (i=1;i<=k;i++) w[i]=read(),mx=max(w[i],mx); if (k>3){ for (i=0;i<=mx;i++){ mi[i][0]=1; for (j=1;j<=k;j++) mi[i][j]=mi[i][j-1]*i%mo; } for (i=0;i<=k;i++) for (j=0;j<=mx;j++) sum[j][i]=(sum[j-1][i]+mi[j][i])%mo; } for (i=1;i<=n;i++){ c=read();d=read(); e[c]+=d; for (j=1;j<=k;j++){ l[i][j]=l[i-1][j]; r[i][j]=r[i-1][j]; } l[i][c]=min(l[i][c],e[c]); r[i][c]=max(r[i][c],e[c]); } for (i=1;i<=k;i++) if (e[i]!=0||l[n][i]<-w[i]||r[n][i]>w[i]) bzans=1; if (!bzans){ printf("-1\n"); return 0; } for (i=0;i<=n;i++){ temp=1; for (j=1;j<=k;j++) temp=temp*max(0ll,(w[j]-(r[i][j]-l[i][j])))%mo; ans=(ans+temp)%mo; } for (i=1;i<=k;i++) a[i]=w[i]-(r[n][i]-l[n][i]); for (i=1;i<=n;i++) for (j=1;j<=k;j++) l[i][j]=min(0ll,l[i][j]+e[j]-l[n][j]),r[i][j]=max(0ll,r[i][j]+e[j]-r[n][j]); for (i=1;i<=k;i++) b[i]=r[n][i]-l[n][i]; for (i=1;i<=n;i++){ T=1e9; for (p=1;p<=k;p++) if (b[p]) T=min(T,(a[p]-(r[i][p]-l[i][p]))/b[p]); if (T<1) break; f[0][0]=1; for (j=1;j<=k;j++) for (p=0;p<=k;p++){ f[j][p]=f[j-1][p]*(a[j]-(r[i][j]-l[i][j]))%mo; if (p) f[j][p]=(f[j][p]+f[j-1][p-1]*(-b[j]))%mo; } for (p=0;p<=k;p++) ans=(ans+f[k][p]*solve(T,p))%mo; } printf("%lld\n",(ans+mo)%mo); return 0; }