经验值:3200
时间限制:1000毫秒
内存限制:128MB
全国 2020 NOIP试题
不许抄袭,一旦发现,直接清空经验!
题目描述 Description
小 C 喜欢跑步,并且非常喜欢在微信步数排行榜上刷榜,为此他制定了一个刷微信步数的计划。
他来到了一处空旷的场地,处于该场地中的人可以用 k 维整数坐标(a1,a2,…,ak)来表示其位置。场地有大小限制,第 i 维的大小为wi,因此处于场地中的人其坐标应满足 1≤ai≤wi(1≤i≤k)。
小 C 打算在接下来的P=w1×w2×…×wk天中,每天从场地中一个新的位置出发,开始他的刷步数计划(换句话说,他将会从场地中每个位置都出发一次进行计划)。
他的计划非常简单,每天按照事先规定好的路线行进,每天的路线由 n 步移动构成,每一步可以用 ci与di表示:若他当前位于 (a1,a2,…,aci,…,ak)则这一步他将会走到 (a1,a2,…,aci+di,…,ak),其中 1≤ci≤k,di∈{−1,1}。小 C 将会不断重复这个路线,直到他走出了场地的范围才结束一天的计划。(即走完第 n 步后,若小 C 还在场内,他将回到第 1 步从头再走一遍)。
小 C 对自己的速度非常有自信,所以他并不在意具体耗费的时间,他只想知道 P 天之后,他一共刷出了多少步微信步数。请你帮他算一算。
输入描述 Input Description
第一行两个用单个空格分隔的整数 n,k。分别表示路线步数与场地维数。
接下来一行 k 个用单个空格分隔的整数 wi,表示场地大小。
接下来 n 行每行两个用单个空格分隔的整数ci,di,依次表示每一步的方向,具体意义见题目描述。
输出描述 Output Description
仅一行一个整数表示答案。答案可能很大,你只需要输出其对 109+7 取模后的值。
若小 C 的计划会使得他在某一天在场地中永远走不出来,则输出一行一个整数 −1。
样例输入 Sample Input
样例1: 3 2 3 3 1 1 2 -1 1 1 样例2: 5 4 6 8 6 5 3 1 2 1 1 1 2 1 2 -1
样例输出 Sample Output
样例1: 21 样例1解释: 从 (1,1) 出发将走 2 步,从 (1,2) 出发将走 4 步,从 (1,3) 出发将走 4 步。 从 (2,1) 出发将走 2 步,从 (2,2) 出发将走 3 步,从 (2,3) 出发将走 3 步。 从 (3,1) 出发将走 1 步,从 (3,2) 出发将走 1 步,从 (3,3) 出发将走 1 步。 共计 21 步。 样例2: 10265
数据范围及提示 Data Size & Hint
测试点编号 | n≤ | k≤ | wi≤ |
---|---|---|---|
1~3 | 5 | 5 | 3 |
4~6 | 100 | 3 | 10 |
7~8 | 105 | 1 | 105 |
9~12 | 105 | 2 | 106 |
13~16 | 5×105 | 10 | 106 |
17~20 | 5×105 | 3 | 109 |
对于所有测试点,保证 1≤n≤5×105,1≤k≤10,1≤wi≤109,di∈{−1,1}。
思路
首先,我们需要根据题意来理解问题。题目给出了小 C 所在的空旷场地的维度和大小,以及小 C 的路线。每天小 C 从场地中一个新的位置出发,按照事先规定好的路线行进,每一步可以向前移动或向后移动一步。小 C 会不断重复这个路线,直到他走出场地范围才结束一天的计划。我们需要计算在 P 天之后,小 C 一共刷出了多少步微信步数。
首先,我们需要根据给出的场地大小,计算出场地中每一个位置的步数。对于每一个维度,步数的变化是循环的。例如,对于第一个维度,从 1 到 w1,每一步的步数是一个循环:1, 2, 3, ..., n, n-1, ..., 1。所以我们可以先计算出每一个维度的步数列表。
接下来,我们需要计算每一天小 C 的总步数。根据题目给出的路线,我们可以发现每一天小 C 的总步数等于每一个维度的步数之积。所以我们可以将每一个维度的步数列表相乘,得到每一天小 C 的总步数。
最后,我们需要计算 P 天内小 C 的总步数。由于小 C 每一天都会重复之前的路线,所以每 P 天的总步数是相等的。所以我们可以将每一天小 C 的总步数乘以 P,得到 P 天内小 C 的总步数。
最后,我们需要对这个结果取模,输出答案。
算法的时间复杂度为 O(n+kw),其中 n 是路线步数,k 是场地的维度,w 是场地中每一个维度的大小。
声明
666,肝了50分钟,码了120行(极简),总算解决了!我的思路没问题,这么长的**不是白费,但代码我只副图片,3149个字母,你们慢慢超吧!哈哈!
e……不逗你们玩了,上代码!
AC CODE
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mod 1000000007
int n,k,w[11],ans=0;
int d[11];
int l[500010][11],r[500010][11];
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){x=(x-y<0?x-y+mod:x-y);}
struct Poly{
int a[11],t;
Poly(){t=0;memset(a,0,sizeof(a));}
void clear(){t=0;memset(a,0,sizeof(a));}
void operator *=(Poly &B){
Poly re;re.t=t+B.t;
for(int i=0;i<=t;i++)
for(int j=0;j<=B.t;j++)
add(re.a[i+j],1ll*a[i]*B.a[j]%mod);
*this=re;
}
}s[11];
int t_max=1e9;
int calc_round_1(){//暴力求解第一轮
int re=0;
for(int i=1;i<=n;i++){
int prod=1;
for(int j=1;j<=k;j++)
if(w[j]-r[i][j]+l[i][j]>0)
prod=1ll*prod*(w[j]-r[i][j]+l[i][j])%mod;
else {prod=0;break;}
add(re,prod);
}
return re;
}
int calc_round_t_max_and_1(){//暴力求解最后一轮
int re=0;
for(int i=1;i<=n;i++){
int prod=1;
for(int j=1;j<=k;j++){
int p=w[j]-max(r[n][j],r[i][j]+d[j])+min(l[n][j],l[i][j]+d[j])-(t_max+1)*abs(d[j]);
if(p>0)prod=1ll*prod*p%mod;
else {prod=0;break;}
}
add(re,prod);
}
return re;
}
int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
struct Lagrange{//自然数幂和板子
int fac[20],inv_fac[20];
Lagrange(){}
void init(){
fac[0]=inv_fac[0]=1;
for(int i=1;i<=15;i++)fac[i]=1ll*fac[i-1]*i%mod;
inv_fac[15]=ksm(fac[15],mod-2);
for(int i=14;i>=1;i--)inv_fac[i]=1ll*inv_fac[i+1]*(i+1)%mod;
}
int pre[20],suf[20];
int calc(int k,int x){
if(!k)return x+1;
int re=0,sum=0;
if(x<=k+2){
for(int i=0;i<=x;i++)add(re,ksm(i,k));
return re;
}
pre[0]=1;for(int i=1;i<=k+2;i++)pre[i]=1ll*pre[i-1]*(x-i)%mod;
suf[k+3]=1;for(int i=k+2;i>=1;i--)suf[i]=1ll*suf[i+1]*(x-i)%mod;
for(int i=1;i<=k+2;i++){
add(sum,ksm(i,k));
(k+2-i&1?dec:add)(re,1ll*sum*pre[i-1]%mod*suf[i+1]%mod
*inv_fac[i-1]%mod*inv_fac[k+2-i]%mod);
}
return re;
}
}Lag;
int cd[20];
int main()
{
scanf("%d %d",&n,&k);ans=1;
for(int i=1;i<=k;i++)
scanf("%d",&w[i]),ans=1ll*ans*w[i]%mod;
for(int i=1,x,y;i<=n;i++){
scanf("%d %d",&x,&y);
d[x]+=y;
for(int j=1;j<=k;j++)
l[i][j]=min(l[i-1][j],d[j]),
r[i][j]=max(r[i-1][j],d[j]);
}
bool tf=false;//判无解
for(int i=1;i<=k;i++)
if(l[n][i]+r[n][i]>=w[i]||d[i]!=0)tf=true;
if(!tf)return puts("-1"),0;
for(int i=1;i<=k;i++)//求t_max
if(w[i]-r[n][i]+l[n][i]<=0){t_max=-2;break;}
else if(d[i]!=0)t_max=min(t_max,(w[i]-r[n][i]+l[n][i])/abs(d[i])-1);
add(ans,calc_round_1());
if(t_max>-2)add(ans,calc_round_t_max_and_1());
if(t_max>=0){
Lag.init();//预处理自然数幂和
for(int i=0;i<=k;i++)
cd[i]=Lag.calc(i,t_max);
for(int i=1;i<=n;i++){
for(int j=1;j<=k;j++){
s[j].clear();//计算出所有多项式,暴力乘起来
s[j].t=d[j]!=0;
s[j].a[0]=w[j]-max(r[n][j],r[i][j]+d[j])+min(l[n][j],l[i][j]+d[j]);
s[j].a[1]=(mod-abs(d[j]))%mod;
if(j>1)s[j]*=s[j-1];
}
for(int j=0;j<=k;j++)
add(ans,1ll*s[k].a[j]*cd[j]%mod);
}
}
printf("%d",ans);
}