NOIP 2020题解(未完待续)

文章目录

  • T1.排水系统
    • 题目描述
    • 思路
    • 代码
  • T2.字符串匹配
    • 题目描述
    • 思路
    • 代码
  • T3.移球游戏
    • 题目描述
    • 思路
    • 代码
  • T4.微信步数
    • 题目描述

T1.排水系统

题目描述

对于一个城市来说,排水系统是极其重要的一个部分。

有一天,小 C 拿到了某座城市排水系统的设计图。排水系统由 n n n 个排水结点(它们从 1 ∼ n 1 \sim n 1n 编号)和若干个单向排水管道构成。每一个排水结点有若干个管道用于汇集其他排水结点的污水(简称为该结点的汇集管道),也有若干个管道向其他的排水结点排出污水(简称为该结点的排出管道)。

排水系统的结点中有 m m m 个污水接收口,它们的编号分别为 1 , 2 , … , m 1, 2, \ldots , m 1,2,,m ,污水只能从这些接收口流入排水系统,并且这些结点没有汇集管道。排水系统中还有若干个最终排水口,它们将污水运送到污水处理厂,没有排出管道的结点便可视为一个最终排水口。

现在各个污水接收口分别都接收了 1 1 1 吨污水,污水进入每个结点后,会均等地从当前结点的每一个排出管道流向其他排水结点,而最终排水口将把污水排出系统。

现在小 C 想知道,在该城市的排水系统中,每个最终排水口会排出多少污水。该城市的排水系统设计科学,管道不会形成回路,即不会发生污水形成环流的情况。

思路

STEP1 拓扑排序
“管道不会形成回路”表示这是一张有向无环图 DAG 。于是结合“每一个排水结点有若干个管道用于汇集其他排水结点的污水”这一条件便很容易想到拓扑排序的思路(事实上也可以用搜索的思路)。先把 m m m 个污水接收口的污水量初始化为 1 1 1 ,再在每轮找到一个入度为零的排水节点,把它有的污水平均分配到它所连的其它排水节点内,最后把所有与它相连的结点入度减一就行了。注意入度为零的排水节点不一定是污水接收口!

STEP2 高精
然而这只能拿到 60 60 60 的好成绩,这题需要高精。但注意到约分后分子 p p p 比分母 q q q 往往小得多,分子只需要unsigned long long就行了。注意到每个节点的排出管道不超过五个,不大于五的质数只有 2 2 2 3 3 3 5 5 5 这三个,分母 q q q 便可以以 p = 2 a 3 b 5 c p=2^a3^b5^c p=2a3b5c 的形式存储,输出答案时只需要高精快速幂就行了。

STEP3 分数加法
假设现在有两个分数 p 1 q 1 \frac{p_1}{q_1} q1p1 p 2 q 2 \frac{p_2}{q_2} q2p2 要相加,则有 p 1 q 1 + p 2 q 2 = p 1 q 2 + p 2 q 1 q 1 q 2 \frac{p_1}{q_1}+\frac{p_2}{q_2}=\frac{p_1q_2+p_2q_1}{q_1q_2} q1p1+q2p2=q1q2p1q2+p2q1
然而这样会面临着分子爆unsigned long long的风险,因此需要同时约分。我们考虑所有包含 2 2 2 的引子,则原式可化为 2 a 2 x 1 + 2 a 1 x 2 2 a 1 + a 2 x 3 \frac{2^{a_2}x_1+2^{a_1}x_2}{2^{a_1+a_2}x_3} 2a1+a2x32a2x1+2a1x2 的形式。不妨设 a 1 ≤ a 2 a_1\le a_2 a1a2,则上下可以约掉 2 a 1 2^{a_1} 2a1 ,即化为 原 式 = 2 a 1 ( 2 a 2 − a 1 x 1 + x 2 ) 2 a 1 ( 2 a 2 x 3 ) = 2 a 2 − a 1 x 1 + x 2 2 a 2 x 3 原式=\frac{2^{a_1}(2^{a_2-a_1}x_1+x_2)}{2^{a_1}(2^{a_2}x_3)}=\frac{2^{a_2-a_1}x_1+x_2}{2^{a_2}x_3} =2a1(2a2x3)2a1(2a2a1x1+x2)=2a2x32a2a1x1+x2这样就能够巧妙地解决精度问题。对于 3 3 3 5 5 5 也有类似讨论,这里不再赘述。

代码

#include
#include
#define ll unsigned long long
using namespace std;
int n,m,to[100001][6],rd[100001];
int stk[100001],top;
ll pw[6][1001];
ll qpw(ll x,int y){
       //记忆化求幂,所有求幂复杂度总共是线性的
	if(pw[x][y]==0) return pw[x][y]=qpw(x,y-1)*x;
	else return pw[x][y];
}
struct num{
       //分母
	int a,b,c;
	num(){
     
		a=b=c=0;
		return;
	}
};
struct big_num{
       //高精
	int x[51],len;
	void operator=(ll a){
     
		len=-1;
		while(a) x[++len]=a%10,a/=10;
		return;
	}
	void write(){
     
		for(int i=len;i>=0;i-=1) printf("%d",x[i]);
		printf("\n");
		return;
	}
};
big_num operator*(big_num a,big_num b){
       //高精乘
	big_num c;
	int z=0,l,r;
	c.len=a.len+b.len;
	for(int i=0;i<=c.len;i+=1){
     
		c.x[i]=z;
		l=max(0,i-b.len); r=min(i,a.len);
		for(int j=l;j<=r;j+=1){
     
			c.x[i]+=a.x[j]*b.x[i-j];
		}
		z=c.x[i]/10;
		c.x[i]%=10;
	}
	while(z) c.x[++c.len]=z%10,z/=10;
	return c;
}
struct frac{
       //分数
	ll p;
	num q;
	void operator=(ll x){
     
		p=x;
		return;
	}
	void deal(){
       //约分
		while(q.a&&p%2llu==0llu){
     
			p/=2llu;
			q.a-=1;
		}
		while(q.b&&p%3llu==0llu){
     
			p/=3llu;
			q.b-=1;
		}
		while(q.c&&p%5llu==0llu){
     
			p/=5llu;
			q.c-=1;
		}
		return;
	}
	void write(){
     
		big_num x,y;
		x=qpw(2ll,q.a); y=qpw(3ll,q.b);
		x=x*y; y=qpw(5ll,q.c); x=x*y;
		printf("%llu ",p);
		x.write();
		return;
	}
}f[100001];
frac operator/(frac x,int y){
       //均分
	if(y==2) x.q.a+=1;
	if(y==3) x.q.b+=1;
	if(y==4) x.q.a+=2;
	if(y==5) x.q.c+=1;
	x.deal();  //约分
	return x;
}
frac operator+(frac x,frac y){
       //分数加
	frac z;
	ll v=x.p,w=y.p;
	z.p=0llu;
	if(x.q.a>=y.q.a) w*=qpw(2llu,x.q.a-y.q.a);
	else v*=qpw(2llu,y.q.a-x.q.a);
	if(x.q.b>=y.q.b) w*=qpw(3llu,x.q.b-y.q.b);
	else v*=qpw(3llu,y.q.b-x.q.b);
	if(x.q.c>=y.q.c) w*=qpw(5llu,x.q.c-y.q.c);
	else v*=qpw(5llu,y.q.c-x.q.c);
	z.p=v+w;
	z.q.a=max(x.q.a,y.q.a);
	z.q.b=max(x.q.b,y.q.b);
	z.q.c=max(x.q.c,y.q.c);
	z.deal();  //约分
	return z;
}
int main(){
     
//	freopen("water.in","r",stdin);
//	freopen("water.out","w",stdout);
	int x,y;
	pw[2][0]=pw[3][0]=pw[5][0]=1llu;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i+=1){
     
		if(i<=m) f[i]=1llu;  //初始水量
		else f[i]=0llu;
		scanf("%d",&to[i][0]);
		for(int j=1;j<=to[i][0];j+=1){
     
			scanf("%d",&to[i][j]);
			rd[to[i][j]]+=1;
		}
	}
	for(int i=1;i<=n;i+=1){
     
		if(!rd[i]) stk[++top]=i;
	}
	while(top){
       //拓扑排序
		x=stk[top--];
		if(to[x][0]) f[x]=f[x]/to[x][0];
		for(int i=1;i<=to[x][0];i+=1){
     
			y=to[x][i];
			rd[y]-=1;
			if(!rd[y]) stk[++top]=y;
			f[y]=f[x]+f[y];  //累计
		}
	}
	int cnt=0;
	for(int i=1;i<=n;i+=1){
     
		if(!to[i][0]) f[i].write();  //输出
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

T2.字符串匹配

题目描述

小 C 学习完了字符串匹配的相关内容,现在他正在做一道习题。

对于一个字符串 S S S,题目要求他找到 S S S 的所有具有下列形式的拆分方案数:

S = A B C S = ABC S=ABC S = A B A B C S = ABABC S=ABABC S = A B A B … A B C S = ABAB \ldots ABC S=ABABABC,其中 A A A B B B C C C 均是非空字符串,且 A A A 中出现奇数次的字符数量不超过 C C C 中出现奇数次的字符数量。

更具体地,我们可以定义 A B AB AB 表示两个字符串 A A A B B B 相连接,例如 A = aab A = \texttt{aab} A=aab B = ab B = \texttt{ab} B=ab,则 A B = aabab AB = \texttt{aabab} AB=aabab

并递归地定义 A 1 = A A^1=A A1=A A n = A n − 1 A^n = A^{n - 1} An=An1 n ≥ 2 n \ge 2 n2 且为正整数)。例如 A = abb A = \texttt{abb} A=abb,则 A 3 = abbabbabb A^3=\texttt{abbabbabb} A3=abbabbabb

则小 C 的习题是求 S = ( A B ) i C S = {(AB)}^iC S=(AB)iC 的方案数,其中 F ( A ) ≤ F ( C ) F(A) \le F(C) F(A)F(C) F ( S ) F(S) F(S) 表示字符串 S S S 中出现奇数次的字符的数量。两种方案不同当且仅当拆分出的 A A A B B B C C C 中有至少一个字符串不同。

小 C 并不会做这道题,只好向你求助,请你帮帮他。

思路

假设 S S S 下标从 1 1 1 开始,长度为 l e n len len,即 S [ 1 … l e n ] S[1\dots len] S[1len]
STEP1 直接暴力
这个思路其实非常好想。首先从头枚举 i i i 表示 A B = S [ 1 … i ] AB=S[1\dots i] AB=S[1i],再枚举可能的出现的次数 j j j,这里可以用字符串哈希,那么 C = S [ i ∗ j + 1 … l e n ] C=S[i*j+1\dots len] C=S[ij+1len]。最后找到所有的 A = S [ 1 … k ] A=S[1\dots k] A=S[1k] 1 ≤ k < i 1\le k< i 1k<i)满足 F ( A ) ≤ F ( C ) F(A)\le F(C) F(A)F(C) 并累计到答案中就行了。注意到拆分出来的第一个字符串和最后一个字符串分别是 A A A C C C,便可以提前以 O ( 2 ∗ l e n ) O(2*len) O(2len) 的复杂度处理好 F F F 数组。
STEP2 特殊性质
若最大次数为 j m a x j_{max} jmax D D D 为剩下的字符串,则 S = ( A B ) j m a x D S=(AB)^{j_{max}}D S=(AB)jmaxD。不难发现 C C C 可以写成 ( A B ) j m a x − j D (AB)^{j_{max}-j}D (AB)jmaxjD 的形式。
不难发现,两个 A B AB AB 平在一起后所有出现奇数次的字符都被抵消,换句话说, F ( ( A B ) 2 ) = 0 F((AB)^2)=0 F((AB)2)=0,那么就可以得到 F ( D ) = F ( ( A B ) 2 D ) = f ( ( A B ) 4 D ) = … F(D)=F((AB)^2D)=f((AB)^4D)=\dots F(D)=F((AB)2D)=f((AB)4D)= F ( A B D ) = F ( ( A B ) 3 D ) = F ( ( A B ) 5 D ) = … F(ABD)=F((AB)^3D)=F((AB)^5D)=\dots F(ABD)=F((AB)3D)=F((AB)5D)=。因此我们只要统计 C = D C=D C=D C = A B D C=ABD C=ABD 的答案,并分别乘上 F F F 相同的情况数就行了。
STEP3 树状数组
我们发现,统计符合条件的 A A A 时,还需要枚举 k k k,这就大大降低了效率。但我们要求的是 F F F 不大于某个值的前缀字符串个数。令 c n t [ i ] [ j ] cnt[i][j] cnt[i][j] 表示枚举到当前位置 i i i 时,满足 F ( S [ 1 … k ] ≤ i ) F(S[1\dots k]\le i) F(S[1k]i) 1 ≤ k ≤ i 1\le k\le i 1ki k k k 的个数。在每一轮时,初始化 c n t [ i ] [ j ] = c n t [ i − 1 ] [ j ] cnt[i][j]=cnt[i-1][j] cnt[i][j]=cnt[i1][j] 如果当前有 F ( S [ 1 … i ] ) = j F(S[1\dots i])=j F(S[1i])=j,则把 c n t [ i ] [ j … 26 ] cnt[i][j\dots 26] cnt[i][j26] 都加上 1 1 1 就行了。不难发现,这个数组的前一维可以去掉。进一步的,把 c n t [ j … 26 ] cnt[j\dots 26] cnt[j26] 这段区间都加上 1 1 1 可以看成把它的差分数组第 j j j 位加上 1 1 1,便可以用树状数组来求前缀和。

代码

#include
#include
#include
#define ull unsigned long long
using namespace std;
ull base=131llu,h[1050000],pow;
char s[1050000];
int t,len,k,tot;
ull ans,cnt[1050001];
int f[1050000],isodd[26];
int lowbit(int x){
     
	return x&(-x);
}
void update(int x){
       //单点加一
	for(x;x<=27;x+=lowbit(x)) cnt[x]+=1;
	return;
}
int sum(int x){
       //前缀和
	int res=0;
	for(x;x;x-=lowbit(x)) res+=cnt[x];
	return res;
}
int main(){
     
//	freopen("string.in","r",stdin);
//	freopen("string.out","w",stdout);
	scanf("%d",&t);
	while(t--){
     
		ans=0llu;
		scanf("%s",s+1);
		len=strlen(s+1);
		for(int i=0;i<26;i+=1) isodd[i]=cnt[i+1]=0;
		for(int i=1;i<=len+1;i+=1) f[i]=1;  //多组数据初始化
		for(int i=len;i>=1;i-=1){
     
			isodd[s[i]-'a']^=1;
			f[i]=f[i+1]+(isodd[s[i]-'a']? 1:-1);  //F(S[i...len])
		}
		for(int i=1;i<=len;i+=1) h[i]=h[i-1]*base+s[i]-'a';
		for(int i=0;i<26;i+=1) isodd[i]=0;
		isodd[s[1]-'a']=1;
		update(tot=2);  
		//树状数组下标不能为零,tot表示当前出现奇数次字符个数加一
		pow=base*base;
		for(int i=2;i<=len-1;i+=1){
       
			k=1;
			for(int j=i*2;j<=len-1;j+=i){
       //求出最大循环次数
				if(h[j]!=h[j-i]*pow+h[i]) break;
				k+=1;
			}
			ans+=(k+1llu)/2llu*sum(f[i*k+1]);  //两种情况
			ans+=k/2llu*sum(f[i*(k-1)+1]);
			isodd[s[i]-'a']^=1;
			tot+=(isodd[s[i]-'a']? 1llu:-1llu);
			update(tot);  //更新cnt
			pow*=base;
		}
		printf("%lld\n",ans);  //输出答案
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

T3.移球游戏

题目描述

小 C 正在玩一个移球游戏,他面前有 n + 1 n + 1 n+1 根柱子,柱子从 1 ∼ n + 1 1 \sim n + 1 1n+1 编号,其中 1 1 1 号柱子、 2 2 2 号柱子、……、 n n n 号柱子上各有 m m m 个球,它们自底向上放置在柱子上, n + 1 n + 1 n+1 号柱子上初始时没有球。这 n × m n \times m n×m 个球共有 n n n 种颜色,每种颜色的球各 m m m 个。

初始时一根柱子上的球可能是五颜六色的,而小 C 的任务是将所有同种颜色的球移到同一根柱子上,这是唯一的目标,而每种颜色的球最后放置在哪根柱子则没有限制。

小 C 可以通过若干次操作完成这个目标,一次操作能将一个球从一根柱子移到另一根柱子上。更具体地,将 x x x 号柱子上的球移动到 y y y 号柱子上的要求为:

  1. x x x 号柱子上至少有一个球;
  2. y y y 号柱子上至多有 m − 1 m - 1 m1 个球;
  3. 只能将 x x x 号柱子最上方的球移到 y y y 号柱子的最上方。

小 C 的目标并不难完成,因此他决定给自己加加难度:在完成目标的基础上,使用的操作次数不能超过 820000 820000 820000。换句话说,小 C 需要使用至多 820000 820000 820000 次操作完成目标。

小 C 被难住了,但他相信难不倒你,请你给出一个操作方案完成小 C 的目标。合法的方案可能有多种,你只需要给出任意一种,题目保证一定存在一个合法方案。

思路

这道题只需要找到一种移球的方法,按照它模拟就好了。下面提供其中一种思路。
我们先看到 n = 2 n=2 n=2 的情况。比方说有下面这种情况:
NOIP 2020题解(未完待续)_第1张图片
我们以 c n t cnt cnt 表示 1 1 1 号柱子上颜色为 1 1 1 的球的个数。那么显然有 c n t = 3 cnt=3 cnt=3。于是先把 2 2 2 号柱子上的前 c n t cnt cnt 个球移动到 3 3 3 号柱子上,就得到了下图。
NOIP 2020题解(未完待续)_第2张图片
接着就从上往下依次考虑 1 1 1 号柱子上的球,如果颜色为 1 1 1 就把它移到 2 2 2 号柱子上去;否则移到 3 3 3 号柱子上去。
NOIP 2020题解(未完待续)_第3张图片
于是我们就会发现 2 2 2 号柱子上前 c n t cnt cnt 个球都是 1 1 1 3 3 3 号柱子上前 m − c n t m-cnt mcnt 个球都是 2 2 2。接着我们就把 2 2 2 号柱子上的前 c n t cnt cnt 个球和 3 3 3 号柱子上的前 m − c n t m-cnt mcnt 个球按类别移回 1 1 1 号柱子上,接着把 3 3 3 号柱子剩下的球都移到 2 2 2 号柱子上。
NOIP 2020题解(未完待续)_第4张图片
到这里我们实际上是给 1 1 1 号柱子上球排个序。接着我们把 1 1 1 号柱子上的两种类型的球分开到 1 1 1 号和 3 3 3 号柱子上。
NOIP 2020题解(未完待续)_第5张图片
接下来只要把 2 2 2 号柱子上的球分开就行了。

对于 n ≥ 3 n\ge 3 n3 的情况,我们每一轮把同种颜色的球移到一起,问题就转变成颜色总数为 n − 1 n-1 n1 的子问题。我们把当前要聚集的颜色看作 1 1 1 其它都看作零。于是两步就可以完成每一轮移动。
STEP1 构造全零列
对于下面这种情况,记录 c n t cnt cnt 为当前 1 1 1 号柱子上颜色为 1 1 1 的球的个数,则有 c n t = 2 cnt=2 cnt=2
NOIP 2020题解(未完待续)_第6张图片
类似于上面 n = 2 n=2 n=2 的讨论,我们也可以把 1 1 1 中的 1 1 1 0 0 0 利用 3 3 3 号柱子和 4 4 4 号柱子分开(标红的是原 1 1 1 号柱子上的球):
NOIP 2020题解(未完待续)_第7张图片
接着我们把 4 4 4 号柱子上的 m − c n t m-cnt mcnt 个编号为零的球移到 1 1 1 号柱子上去:
NOIP 2020题解(未完待续)_第8张图片
我们再考虑 2 2 2 号柱子。把其中编号为零的球移到 1 1 1 号柱子上直到填满为止,剩下的球都移动到 4 4 4 号柱子上。
NOIP 2020题解(未完待续)_第9张图片
如图所示(标红的是原 2 2 2 号柱子上的球),第一列就是一个全零列。事实上,由于原来 1 1 1 号和 2 2 2 号柱子上为零的球的个数必定大于 m ( 1 号 柱 子 ) + m ( 2 号 柱 子 ) − m ( 最 大 可 能 有 编 号 为 1 的 球 的 个 数 ) = m m(1号柱子)+m(2号柱子)-m(最大可能有编号为1的球的个数)=m m(1)+m2m1=m,也就是说必能构造全零列。
STEP2 构造全一列

代码

#include
#include
using namespace std;
int n,m,ans,ansx[820001],ansy[820001];
int a[52][401],p[52],cnt;
void move(int x,int y){
     
	a[y][++a[y][0]]=a[x][a[x][0]--];
	if(ansx[ans]==y&&ansy[ans]==x) ans-=1;
	else ansx[++ans]=x,ansy[ans]=y;
	return;
}
int chk(int x,int y){
     
	int res=0;
	for(int i=1;i<=a[x][0];i+=1) res+=(a[x][i]==y);
	return res;
}
int main(){
     
//	freopen("ball.in","r",stdin);
//	freopen("ball.out","w",stdout);
	scanf("%d%d",&n,&m);
	p[n+1]=n+1;
	for(int i=1;i<=n;i+=1){
     
		a[i][0]=m; p[i]=i;
		for(int j=1;j<=m;j+=1) scanf("%d",&a[i][j]);
	}
	for(int i=n;i>=3;i-=1){
     
		cnt=chk(p[1],i);
		for(int j=1;j<=cnt;j+=1){
     
			move(p[i],p[i+1]);
		}
		for(int j=m;j>=1;j-=1){
     
			if(a[p[1]][j]==i) move(p[1],p[i]);
			else move(p[1],p[i+1]);
		}
		for(int j=1;j<=m-cnt;j+=1) move(p[i+1],p[1]);
		for(int j=m;j>=1;j-=1){
     
			if(a[p[2]][j]!=i&&a[p[1]][0]<m) move(p[2],p[1]);
			else move(p[2],p[i+1]);
		}
		swap(p[2],p[i+1]); swap(p[1],p[i]);
		for(int j=1;j<i;j+=1){
     
			cnt=chk(p[j],i);
			for(int k=1;k<=cnt;k+=1) move(p[i],p[i+1]);
			for(int k=m;k>=1;k-=1){
     
				if(a[p[j]][k]==i) move(p[j],p[i]);
				else move(p[j],p[i+1]);
			}
			swap(p[j],p[i]); swap(p[i],p[i+1]);
		}
		for(int j=1;j<i;j+=1){
     
			for(int k=m;k>=1;k-=1){
     
				if(a[p[j]][k]==i) move(p[j],p[i+1]);
				else break;
			}
			while(a[p[j]][0]<m) move(p[i],p[j]);
		}
	}
	cnt=chk(p[1],1);
	for(int i=1;i<=cnt;i+=1) move(p[2],p[3]);
	for(int i=m;i>=1;i-=1){
     
		if(a[p[1]][i]==1) move(p[1],p[2]);
		else move(p[1],p[3]);
	}
	for(int i=1;i<=cnt;i+=1) move(p[2],p[1]);
	for(int i=1;i<=m-cnt;i+=1) move(p[3],p[1]);
	for(int i=1;i<=cnt;i+=1) move(p[3],p[2]);
	for(int i=1;i<=m-cnt;i+=1) move(p[1],p[3]);
	for(int i=m;i>=1;i-=1){
     
		if(a[p[2]][i]==1) move(p[2],p[1]);
		else move(p[2],p[3]);
	}
	printf("%d\n",ans);
	for(int i=1;i<=ans;i+=1) printf("%d %d\n",ansx[i],ansy[i]);
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

T4.微信步数

题目描述

小 C 喜欢跑步,并且非常喜欢在微信步数排行榜上刷榜,为此他制定了一个刷微信步数的计划。

他来到了一处空旷的场地,处于该场地中的人可以用 k k k 维整数坐标 ( a 1 , a 2 , … , a k ) (a_1, a_2, \ldots , a_k) (a1,a2,,ak) 来表示其位置。场地有大小限制,第 i i i 维的大小为 w i w_i wi ,因此处于场地中的人其坐标应满足 1 ≤ a i ≤ w i 1 \le a_i \le w_i 1aiwi 1 ≤ i ≤ k 1 \le i \le k 1ik)。

小 C 打算在接下来的 P = w 1 × w 2 × ⋯ × w k P = w_1 \times w_2 \times \cdots \times w_k P=w1×w2××wk 天中,每天从场地中一个新的位置出发,开始他的刷步数计划(换句话说,他将会从场地中每个位置都出发一次进行计划)。

他的计划非常简单,每天按照事先规定好的路线行进,每天的路线由 n n n 步移动构成,每一步可以用 c i c_i ci d i d_i di 表示:若他当前位于 ( a 1 , a 2 , … , a c i , … , a k ) (a_1, a_2, \ldots , a_{c_i}, \ldots, a_k) (a1,a2,,aci,,ak),则这一步他将会走到 ( a 1 , a 2 , … , a c i + d i , … , a k ) (a_1, a_2, \ldots , a_{c_i} + d_i, \ldots , a_k) (a1,a2,,aci+di,,ak),其中 1 ≤ c i ≤ k 1 \le c_i \le k 1cik d i ∈ { − 1 , 1 } d_i \in \{-1, 1\} di{ 1,1}。小 C 将会不断重复这个路线,直到他走出了场地的范围才结束一天的计划。(即走完第 n n n 步后,若小 C 还在场内,他将回到第 1 1 1 步从头再走一遍)。

小 C 对自己的速度非常有自信,所以他并不在意具体耗费的时间,他只想知道 P P P 天之后,他一共刷出了多少步微信步数。请你帮他算一算。


预祝大家来今后的比赛中取得优异的成绩!

谢谢观看!

你可能感兴趣的:(题解,c++)