HPU算法协会公开课第二期: 【基础算法2】 题解
比赛链接
比赛密码:
HPUACM
A - Pseudoprime numbers
英文题*1:Fermat’s theorem states that for any prime number p and for any integer a > 1, ap=a(mod p).
费马定理指出,对于任意素数p和任意整数a>1,ap=a(mod p)。也就是说,如果我们把a提升到ap ,除以p,余数就是a。p的一些(但不是很多)非素数,称为“基于a的伪素数”(base-a pseudoprimes),有a的这个性质。(而有些被称为是Carmichael数的,对所有a都是base-a pseudoprimes)。
给定2<p≤1000000000 和 1<a<p,确定p是否是“基于a的伪素数”(base-a pseudoprimes)。
Input
输入包含几个测试用例,后跟一行包含“0 0”。每个测试用例由一行包含p和a的代码组成。
Output
对于每个测试用例,如果p是一个base-a pseudoprimes,则输出“yes”;否则输出“no”。
Sample Input
3 2
10 3
341 2
341 3
1105 2
1105 3
0 0
Sample Output
no
no
yes
no
yes
yes
分析
这题目说的花里胡哨的,其实的意思就是看p满不满足两个条件:①apmod p=a;②p不是素数。如果满足这两个条件就是题目说的base-a pseudoprimes,不满足其中一条或者全都不满足就不是。
对于条件①,可以选择快速幂的方法。如果先算ap的话,极有可能产生溢出的现象,而每次求都求一次余,那么每次计算出来的都是小于p的余数,这样就减少了溢出的可能性。
- 快速幂模板:
对于条件②,素数就是除了1和它本身外没有其他因子的数,并且1也不是素数。因此只需要看i∈[2, x \sqrt{x} x]时,x是否能与i整除(取余等于0)。typedef long long ll ; ll poww ( ll x , ll y , ll mod ) { ll ans = 1; while ( y ) { if( y&1 ) ans =( ans * x ) % mod ; x =( x * x ) % mod ; y >>= 1; } return ans ; }
- 素数模板:
↑写这个模板的时候呆了一下,忘记写#include<cmath> typedef long long ll ; bool check_Prime(ll x) { for(int i=2;i<int(sqrt(x)+1);i++) { if(x%i==0) return false; } if(x != 1) return true; else return false; //1不是素数 }
#include<cmath>
了,然后就CE辽。一定要记得加上这个库嗷!
看那个p的最大情况是1e9,所以这题定义时选择使用long long
。
代码
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
long long P,A;
bool check_Prime(long long x) //看看是不是素数,是返回true,不是返回false
{
for(int i=2;i<int(sqrt(x)+1);i++)
{
if(x%i==0) return false;
}
if(x != 1) return true;
else return false;
}
long long poww(long long a,long long b,long long mod) //快速幂
{
long long ans=1;
while(b)
{
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
int main()
{
while(~scanf("%lld%lld",&P,&A))
{
if(P==0 && A==0) break;
if(!check_Prime(P)) //如果不是素数,满足条件2
{
if(poww(A,P,P)==A) //满足条件1
{
printf("yes\n");
}
else printf("no\n"); //不满足条件1
}
else printf("no\n"); //是素数的话就不满足条件2了
}
}
B - Raising Modulo Numbers
英文题*2:People are different.
每个玩家选择两个数字Ai和Bi,并将它们写在纸条上。其他人看不到数字。在一个特定的时刻,所有的玩家向其他人展示他们的数字。目标是确定所有玩家包括自己的所有表达式之和,并在除以给定的M后确定余数。胜者是首先确定正确结果的人。根据玩家的经验,选择更高的数字可能会增加难度。
你应该写一个程序,计算结果,并能够找出谁赢得了比赛。
(这里翻译看不懂也问题不大,看后面就星)
Input
输入包括Z个赋值。它们的数目由出现在输入第一行的单个正整数Z给出。然后是一些赋值。每个赋值以包含整数 M(1<=M<=45000) 的行开始。总数将除以这个数字。下一行包含玩家数 H(1<=H<=45000) 。接下来就是H行。在每一行上,正好有两个数字Ai和Bi被空格隔开。两个数字不能同时等于零。
Output
对于每组赋值,只有一行输出。在这一行,有一个数字,表示表达式 (A1B1+A2B2+…+AHBH)mod M 的结果
Sample Input
3
16
4
2 3
3 4
4 5
5 6
36123
1
2374859 3029382
17
1
3 18132
Sample Output
2
13195
13
分析
这题看懂了题意就会发现和上一道题差不多,都是要用快速幂求模运算。而与上一题不同的地方是,对于每组数据,上一题只需要求单个apmod p就可以了,而这道题需要求(A1B1+A2B2+…+AHBH)mod M,有多个数据相加求模。这里就要直到模运算的一些性质:
- (a + b) % p = (a % p + b % p) % p
- (a – b) % p = (a % p – b % p) % p
- (a × b) % p = (a % p × b % p) % p
- (a ^ b) % p = ((a % p) ^ b) % p
结合律:- ((a+b) % p + c) % p = (a + (b+c) % p) % p
- ((a×b) % p × c) % p = (a × (b×c) % p) % p
交换律:- (a + b) % p = (b + a) % p
- (a × b) % p = (b × a) % p
分配律:- ((a +b)% p × c) % p = ((a × c) % p + (b × c) % p) % p
这道题就用了第1个性质。
代码
#include<cstdio>
using namespace std;
long long Z,M,H,sum;
long long A[50000],B[50000],u,v;
long long poww(long long a,long long b,long long mod) //快速幂
{
long long ans=1;
while(b)
{
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
int main()
{
scanf("%lld",&Z);
while(Z--)
{
scanf("%lld",&M);
scanf("%lld",&H);
sum=0;
for(int i=0;i<H;i++) //有H个玩家
{
scanf("%lld%lld",&u,&v);
A[i]=u;
B[i]=v;
}
/*对sum的赋值与输出*/
for(int i=0;i<H;i++)
{
sum=sum+poww(A[i],B[i],M);
}
printf("%lld\n",sum%M);
}
}
最后对sum的赋值与输出也可以写成:
for(int i=0;i<H;i++)
{
sum=(sum+poww(A[i],B[i],M))%M;
}
printf("%lld\n",sum);
意义是一样的。
C - Key Set
英文题*3:soda has a set S with n integers {1,2,…,n}.
soda的集合S有n个整数{1,2,…,n}。如果集合中的整数和是偶数,则集合称为键集合(key set)。他想知道有多少非空的S子集是键集合(key set)。
Input
有多个测试用例。输入的第一行包含一个整数 T(1≤T≤105),表示测试用例的数量。对于每个测试用例:
第一行包含整数 n(1≤n≤109),即集合中的整数数。
Output
对于每个测试用例,输出模1000000007的键集合(key set)数。
Sample Input
4
1
2
3
4
Sample Output
0
1
3
7
分析
这题的意思是:当输入n后,计算{1,2,…,n}这个集合中有多少子集合,使得子集合里的元素是偶数。
举例子看下:
个数与n的关系是2n-1-1,但得出这个结论是举例子找出来的规律。个数与个数间相差2的幂次,因此推断出关系与2n-1有关,而个数与2的幂次都相差1,因此要-1,得到2n-1-1。(这里解释的可能不是很清楚,需要自己理解一下)
发现关系有ab这样的格式之后,再加上题目要求输出模1000000007的键集合(key set)数,我们就考虑使用快速幂来进行计算。要注意的是,在最后计算完毕后,应当把计算得出的值-1,才是我们找出的规律的值。
代码
#include<cstdio>
using namespace std;
int T;
long long N,sum;
long long poww(long long a,long long b,long long mod) //快速幂
{
long long ans=1;
while(b)
{
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans-1; //要记得-1噢
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%lld",&N);
sum=poww(2,N-1,1000000007);
printf("%lld\n",sum);
}
}
D - Distribution money
英文题*4:AFA want to distribution her money to somebody.
AFA想把她的钱分配给一些人。她把她的钱分成n份部分。一想得到钱的人可以得到不止一个部分。但是如果一个人的钱比其他人的总和还多,他应该受到惩罚。每个得到一部分钱的人会在那部分写上他的ID。
Input
有多种情况。
对于每种情况,第一行中都有一个整数 n(1<=n<=1000)。
在第二行中,有n个整数a1,a2…an(0<=ai<10000)ai是第i个人的ID。
Output
输出应该受到惩罚的人的ID。
如果没有人应该受到惩罚,输出-1。
Sample Input
3
1 1 2
4
2 1 4 3
Sample Output
1
-1
分析
这题我没用教的二分/快速幂做,用的是最普通的做法。一个人得到的数量比其他人的数量还多就受到惩罚,那么就需要统计一下每个人得到了多少数量(a[t]),并把它与其他人的数量(N-a[t])进行比较。如果数量达到惩罚的标准,就记录下这个人的ID。
比较这个人的数量是否比其他人的数量还多时,可以写成if(a[i]>N-a[i])
,也可以写成if(a[i]>N/2)
,这两个是一样的。(前一个可能好理解一点而已)
使用memset(a,0,sizeof(a))
的时候要记得加上头文件<cstring>
或<memory.h>
噢
代码
#include<cstdio>
#include<cstring>
using namespace std;
int N,a[10010],t,cnt;
int main()
{
while(~scanf("%d",&N))
{
memset(a,0,sizeof(a)); //初始化数组
cnt=-1; //如果没找到的话就一直是-1
for(int i=0;i<N;i++) //记录每个人得到了多少
{
scanf("%d",&t);
a[t]++;
}
for(int i=0;i<10001;i++)
{
if(a[i]>N-a[i]) //这里可以写成if(a[i]>N/2)
{
cnt=i; //记录需要受到惩罚的人的ID号
break;
}
}
printf("%d\n",cnt);
}
}
E - Rightmost Digit
英文题*5:Given a positive integer N, you should output the most right digit of N^N.
给定一个正整数N,你应该输出N^N的最右边的数字。
Input
输入包含几个测试用例。输入的第一行是一个整数T,它是测试用例的数量。接下来是T个测试用例。
每个测试用例都包含一个正整数 N(1<=N<=100000000)。
Output
对于每个测试用例,您应该输出N^N的最右边的数字。
Sample Input
2
3
4
Sample Output
7
6
Hint
在第一种情况下, 3 × 3 × 3 = 27, 所以最右边的数字是7。
在第二种情况下,4 × 4 × 4 × 4 = 256, 所以最右边的数字是6。
分析
求NN最右边的数字,分为两步:①求NN的值;②求NN最右边的数字。
为了实现这两种要求,可以使用快速幂。由于要求最右边的数字,即求个位上的数字,就需要把mod的值定为10(对10求余就是个位上的数字)。
代码
#include<cstdio>
using namespace std;
int T;
long long N;
long long poww(long long x,long long y,long long mod) //快速幂
{
long long ans=1;
while(y)
{
if(y&1) ans=(ans*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%lld",&N);
printf("%lld\n",poww(N,N,10));
}
}
F - 人见人爱A^B
求A^B的最后三位数表示的整数。
说明:A^B的含义是“A的B次方”
Input
输入数据包含多个测试实例,每个实例占一行,由两个正整数A和B组成(1<=A,B<=10000),如果A=0, B=0,则表示输入数据的结束,不做处理。
Output
对于每个测试实例,请输出A^B的最后三位表示的整数,每个输出占一行。
Sample Input
2 3
12 6
6789 10000
0 0
Sample Output
8
984
1
分析
这题正好是龙佬上课的时候让我们写的题。这一题也和上一题一样,只是上一题求的是NN最右边的数字,而这题求的是AB最后三位数表示的整数。但步骤是相通的:①求AB的值;②AB最后三位数表示的整数。
为了实现这两种要求,可以使用快速幂。由于要求最后三位数表示的整数,就需要把mod的值定为1000。
代码
#include<cstdio>
using namespace std;
int A,B,cnt;
int poww(int x,int y,int mod) //快速幂
{
int ans=1;
while(y)
{
if(y&1) ans=(ans*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return ans;
}
int main()
{
while(~scanf("%d%d",&A,&B))
{
if(A==0 && B==0) break;
cnt=poww(A,B,1000);
printf("%d\n",cnt);
}
}
G - Trailing Zeroes (III)
英文题*6:You task is to find minimal natural number N, so that N! contains exactly Q zeroes on the trail in decimal notation.
你的任务是找到最小的自然数N,使得N!在十进制表示法中,尾部正好包含Q个零。如你所知N!=1×2×…×N。例如,5!=120,120在尾部包含一个零。
Input
输入以整数 T(≤10000)开始,表示测试用例的数量。
每种情况在一行中包含一个整数 Q(1≤Q≤108)。
Output
对于每个案例,打印案例编号和N。如果找不到解决方案,则打印“不可能”。
Sample Input
3
1
2
5
Sample Output
Case 1: 5
Case 2: 10
Case 3: impossible
分析
刚开始看这道题我人都傻了,百度翻译翻译的是“轨迹上”有Q个零,我还在纳闷“轨迹上”是啥意思,然后用土方法把1~10的阶乘都列出来了之后,发现5!=120,6!=720,7!=5040,8!=40320,9!=362880,10!=3628800。如果是整体有Q个0的话,那么Q=2的时候N应该等于7而不是10了。因此猜出题目的意思应该是:求出阶乘的末尾有Q个0的最小的N。
可能都怪英语不好……事后发现tail是尾部的意思,可题目上写的不是trail吗??整个人傻住
理解题意是一个问题,还有一个问题就是如何求阶乘末尾0的个数。某数末尾有0,就说明该数是另一个数与10相乘得到的,也就是说是另一个数×2×5得到的。也就是说,某数末尾有几个0,是与某数的因子中有几个2和几个5有关。举个例子:
10!=1×2×3×4×5×6×7×8×9×10,可以发现10!的因子中有1(2)+2(4)+1(6)+3(8)+1(10)=8
个2,以及1(5)+1(10)=2
个5,8个2和2个5相乘得到的数中末尾有2个零,而10!=3628800末尾也有2个零。而且因为是阶乘,所以2的个数必然比5的个数多,因此只需要考虑有几个5就星(因为2的个数铁定够和5相乘,当然想统计2的个数也行,只需要取统计出的两个值中最小的那个值就星辽,也就是取min(count2,count5)
)。
代码
#include<cstdio>
using namespace std;
int T,N;
long long Q,l,r,mid,ans;
long long search(long long x)
{
long long cnt=0;
while(x)
{
cnt+=(x/5);
x/=5;
}
return cnt;
}
int main()
{
scanf("%d",&T);
N=1;
while(T--) //有T组测试用例
{
scanf("%lld",&Q);
l=1,r=1e10+8; //r的值需要开大一点
mid=(l+r)/2;
while(l<r)
{
mid=(l+r)/2;
if(search(mid)>=Q) //>可能好理解一点,=Q还要r=mid是因为现在碰到的数不一定是最小的满足条件的数,因此还需要再找
{
ans=mid;
r=mid; //r=mid是因为mid可能是满足条件的值,不能跳过
}
else l=mid+1; //l=mid+1是因为mid不满足条件,直接把左端点移到mid后面一个就星
}
printf("Case %d: ",N);
N++;
if(search(ans)==Q)
{
printf("%lld\n",ans);
}
else printf("impossible\n");
}
}
H - Pie
英文题*7:My birthday is coming up and traditionally I’m serving pie.
我的生日快到了,按照传统我要做馅饼。不仅仅是一个馅饼,不,我有N个馅饼,口味各异,大小各异。还有F个朋友要来参加我的聚会,他们每人得到一块馅饼。这应该是一块馅饼,而不是几小块,因为看起来很乱。不过,这一块可以是一整块馅饼。
我的朋友们都很容易生气,如果他们中的一个得到了比其他人更大的一块,他们就会开始抱怨。因此,他们所有人都应该得到同样大小(但不一定是同样形状)的馅饼,即使这会导致一些馅饼被糟蹋(这比糟蹋派对要好)。当然,我也想要留一块馅饼给自己,而且那块也应该是同样大小的。
我们能得到的最大尺寸是多少?所有的馅饼都是圆柱形的,它们都有相同的高度1,但是馅饼的半径可以不同。
Input
一行正整数:测试用例的数量。对于每个测试用例:
—一行有两个整数N和F,其中1<=N,F<=10000:馅饼的数目和朋友的数目。
—一行有N个整数ri(1<=ri<=10000),代表pies的半径。
Output
对于每个测试用例,输出一行尽可能大的体积V,这样我和我的朋友都可以得到一个V大小的饼片。答案应该是一个浮点数,绝对误差不超过10^(-3)。
Sample Input
3
3 3
4 3 3
1 24
5
10 5
1 4 2 3 4 5 6 5 4 2
Sample Output
25.1327
3.1416
50.2655
分析
写题5min,debug2h(…•˘_˘•…)这题一直错主要是平常都不怎么注意的精度问题:①Pi的精度不够高,要比平常要求的小数点后的数字多好几位,不然就会WA;②自定义的Answer_search函数中的while(r-l>1e-5),r与l相差的精度也要高,刚开始写1e-4就又双叒叕WA了……由于精度问题与答案错误有很大的差别,于是改bug改了个爽(不
题意大致是要公平地分饼。有N个饼,F个朋友,那么加上自己就一共有F+1个人分饼。每个饼都是高为1的圆柱体,每个人得到的饼尺寸要一致(体积相同),但形状可以不同。分饼时,不能是全部的体积之和÷总人数,因为这样就不满足题目中的“This should be one piece of one pie, not several small pieces since that looks messy”了。应该利用二分法,用二分法取得的中间数mid,看看体积大于等于mid的馅饼与mid相除,得到的个数相加是否大于等于F+1。若是,则找找还有没有体积更大的情况;若不是,则找找有没有体积小点的能满足条件的情况。
这里判断是否满足情况自定义的函数check中,用到了lower bound
函数,lower bound(起始地址,结束地址,要查找的数值) 返回的 是大于或等于要查找的数值的第一个元素位置。直接从体积大于等于mid的馅饼开始找,在一定程度上能减少时间的消耗。不过这里憨了一下:刚开始写的时候把i=lower_bound(V,V+N,x)-V
写成了i=lower_bound(V,V+N,x)-V[0],这两个是有很大区别的啊!V[0]指的是数组V中的第一个元素,而lower_bound返回的是地址,这两个在一起用肯定是不对滴!应该把V[0]改成V,V代表的是数组V的首地址(也就是数组V第一个元素的地址)。首地址-大于等于要查找的数值的第一个元素的地址,就是找出来的元素的下标。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#define Pi 3.1415926535897
using namespace std;
int T,N,F,h=1,R[10010];
double l,r,ave,V[10010];
bool check(double x)
{
int cnt=0;
for(int i=lower_bound(V,V+N,x)-V;i<N;i++)
{
cnt=cnt+(int)(V[i]/x); //第i个馅饼按照x的尺寸去切,最多能分的人数(取整)
}
if(cnt>=F+1) return true;
else return false;
}
double Answer_search(double l,double r)
{
double ans;
double mid=(l+r)/2.0;
while(r-l>1e-5)
{
mid=(l+r)/2.0;
if(check(mid)) //mid满足条件
{
ans=mid;
l=mid; //看看有没有更大的满足条件的体积
}
else r=mid; //mid太大了
}
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&N,&F);
memset(V,0,sizeof(V)); //初始化数组
for(int i=0;i<N;i++) //给数组V赋值
{
scanf("%d",&R[i]);
V[i]=Pi*R[i]*R[i]*h;
}
if(N==1) //如果只有一块饼就只能凑合凑合就一坨人分一块吃
{
ave=V[0]/(F+1.0);
printf("%.4lf\n",ave);
}
else //如果不止一块
{
sort(V,V+N); //对每块馅饼的体积从小到大排序
l=0.0,r=V[N-1]; //l是下界,每个人都分不到;r是上界,每个人都得到整个馅饼(而且这个馅饼是所有馅饼中体积最大的)
ave=Answer_search(l,r);
printf("%.4lf\n",ave);
}
}
}
I - Can you solve this equation?
英文题*8:Now,given the equation 8x4 + 7x3 + 2x2 + 3x + 6 == Y,can you find its solution between 0 and 100;
现在,假设方程8x4+7x3+2x2+3x+6==Y,你能在0到100之间找到它的解吗?现在请试试你的运气。
Input
输入的第一行包含一个整数 T(1<=T<=100),表示测试用例的数量。接着T行,每行有一个实数 Y( fabs(Y)<=1e10 );
Output
对于每个测试用例,您应该只输出一个实数(精确到小数点后4位),这是方程的解;或者,如果0到100之间的方程没有解,就输出“No solution!”
Sample Input
2
100
-4
Sample Output
1.6152
No solution!
分析
要判断0到100之间的方程有没有解,就要先看看方程的情况。设f(x)=8x4+7x3+2x2+3x+6,x∈[0,100]时,f(x)恒>0。f(x)对x求导后,f’(x)=32x3+21x2+4x+3,x∈[0,100]时,f’(x)恒>0。因此可以推断出:f(x)=8x4+7x3+2x2+3x+6在x∈[0,100]这个区间上单调递增。
因为函数f(x)在x∈[0,100]这个区间上是单调递增的,那么如果Y<f(0)或Y>f(100),就说明f(x)==Y在x∈[0,100]这个区间上没有对应的x的解。
由于满足使用二分法的三个条件:①答案区间上下限确定(x∈[0,100]);②检验某值是否可行是个简单活(判断值的大小);③可行解满足区间单调性。因此此题我们使用二分法。
因为一些不可避免的误差问题,判断相等的时候我们不用直接的“==”,而是判断两数相减是否小于一个极小值(1e-10)。
代码
#include<cstdio>
#include<algorithm>
using namespace std;
int T,Y;
double f(double a)
{
return 8*a*a*a*a+7*a*a*a+2*a*a+3*a+6.0;
}
double check(double a,double b,int c)
{
double mid=(a+b)/2.0;
while(b-a>=1e-10)
{
mid=(a+b)/2.0;
if(fabs(f(mid)-c)<1e-10) //f(mid)与c(近似)相等
break;
if(f(mid)>c) //f(mid)比c大,要把右端点缩小
b=mid;
if(f(mid)<c) //f(mid)比c小,要把左端点放大
a=mid;
}
return mid;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&Y);
if(Y<f(0) || Y>f(100)) //没有解
printf("No solution!\n");
else //有解
printf("%.4lf\n",check(0,100,Y));
}
}
J - Subsequence
英文题*9:A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, and a positive integer S (S < 100 000 000) are given.
给出了N个正整数(10<N<100000)的序列,每个正整数小于或等于10000,以及一个正整数 S(S<100000 000) 。编写一个程序,找出序列中连续元素的子序列的最小长度,其总和大于或等于S。
Input
第一行是测试用例的数量。对于每个测试用例,程序必须从第一行读取数字N和S,用间隔隔开。序列号在测试用例的第二行给出,用间隔隔开。输入将在文件结束时结束。
Output
对于每种情况,程序必须在输出的单独行上打印结果文件。如果没有答案,打印0。
Sample Input
2
10 15
5 1 3 5 10 7 4 9 2 8
5 11
1 2 3 4 5
Sample Output
2
3
分析
这里用到了尺取法。计算区间[l,r]的区间和sum,如果sum<S,就把右端点r往后挪一个位置,直到sum>=S,或r超出了区间的范围(没有找到/没有找到更好的)为止。跳出while循环后如果sum<S,说明左端点l已经挪得太后面了(l太大了)——没有找到更好的,或者是没有找到,此时就应当跳出整个大循环(for循环)准备输出结果了。而如果跳出while循环后sum>=S,就说明这可能是更好的情况,将此时的长度与之前储存的最小的长度len进行比较,将二者中较小的那个储存进len里记录下来,然后将左端点l后移,查找是否有更好的情况。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#define INF 100010
using namespace std;
long long T,N,S,a[100010];
long long l,r,sum,len;
int main()
{
scanf("%lld",&T);
while(T--) //有T组测试用例
{
scanf("%lld%lld",&N,&S);
memset(a,0,sizeof(a)); //初始化数组
for(int i=0;i<N;i++)
scanf("%lld",&a[i]);
for(l=0,r=0,sum=0,len=INF;l<=r;)
{
while(sum<S && r<N) //当区间和大于等于S,或右端点已经超过了区间范围时结束循环
{
sum=sum+a[r]; //区间[l,r]的和
r++;
}
if(sum<S) //此时是左端点l太大时
break;
len=min(len,r-l); //当前遇到的子序列的最小长度
sum=sum-a[l]; //左端点往后挪一挪看看行不行
l++;
}
if(len!=INF) //如果找到了答案
printf("%lld\n",len);
else //如果没有找到答案
printf("0\n");
}
}