1. 程式人生 > 實用技巧 >NOIP2020 T4微信步數

NOIP2020 T4微信步數

題目描述

有一個\(k\)維空間,以及一個行走方案,求從空間中每個點出發按行走方案行走走出範圍的步數之和。

題解

這題考試時沒有細想,只打了暴力,時間都拿去肝T3了。

難度其實也跟平時模擬賽做的難題差不多,但的的確確是超出了自己的能力範圍。

首先考慮將步數之和轉化為在走了當前步數時,還有多少個點沒有走出範圍,然後以此統計答案。

容易發現,對於每一層而言,都是最兩側的點最先走出去。並且可以進一步發現,從第二輪開始,每輪走出去的點數都是相同的。

於是可以考慮先處理出第一輪的答案,然後計算出從第二輪開始每一輪走出去的點數。

於是不妨考慮先列舉當前是在某一輪的第\(i\)步,然後考慮當前這一步會在第幾輪讓所有的點都走出範圍。於是會發現對於後半部分會是一個多項式的部分,可以考慮設\(f_{i,j}\)

表示當前乘了\(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;
}