A题
qwb同时也是是之江学院的志愿者,暑期要前往周边地区支教,为了提高小学生的数学水平。她把小学生排成一排,从左至右从1开始依次往上报数。
玩完一轮后,他发现这个游戏太简单了。于是他选了3个不同的数x,y,z;从1依次往上开始报数,遇到x的倍数、y的倍数或z的倍数就跳过。如果x=2,y=3,z=5;第一名小学生报1,第2名得跳过2、3、4、5、6,报7;第3名得跳过8、9、10,报11。
那么问题来了,请你来计算,第N名学生报的数字是多少?
开始还以为就是简单的模拟,然后看了一下数据范围其中0 <= a <1e9,0 < b < 1e9,1 <= n < 1e9。 果断超时。
分析:在1-mid 里面,能被x整除的数据数量为mid/x,以此类推。我们可以二分,数字,然后看前面的人有没有n个,用到容斥原理,最小公倍数。
#include
#include
#include
#include
#include
using namespace std;
long long x,y,z;
long long n;
const long long inf= 1e18;
long long gcd(long long a,long long b)
{
if(b==0) return a;
else return gcd(b,a%b);
}
long long k1=1,k2=1,k3=1,k4=1;
void lcm()
{
long long g1=gcd(x,y);
k1=(x*y)/g1;
long long g2=gcd(y,z);
k2=(y*z)/g2;
long long g3=gcd(x,z);
k3=(x*z)/g3;
k4=k1*z/gcd(k1,z);
return ;
}
int ok(long long mid)
{
long long num=0,gg=0;
if(k4>0) gg=mid/k4;
num=mid-(mid/x+mid/y+mid/z-mid/k1-mid/k2-mid/k3+gg);//这里根据我们的容斥原理,我们分别减去了能被x,y,z整除的数,其中有重复的数,然后我们减去能被他们最小公倍数整除的数就好了,最后加上能被他们三个最小公倍数整除的数。这里,因为他们三个的最小公倍数可能会超龙龙,如果超了,那么应该负数,我们是不用加的(答案在long龙里面)
if(num>=n) return 1;
else return 0;
}
int main()
{
while(scanf("%lld %lld %lld %lld",&x,&y,&z,&n)!=EOF)
{
k1=1,k2=1,k3=1,k4=1;
lcm();
long long l=1,r=1e18,res=1;
while(l<=r)
{
long long mid=l+(r-l)/2;
if(ok(mid)) {
res=mid;
r=mid-1;
}
else l=mid+1 ;
}
// while(r%x==0||r%y==0||r%z==0)
// r++;
printf("%lld\n",res);
}
return 0;
}
B题
做完了辣么多的数学题,qwb好好睡了一觉。但是他做了一个梦:
有一个n*m的矩阵,qwb在这个矩阵的左上角(1,1),终点在右下角(n,m)。
每个格子中有小钱钱,也可能没有,还有可能是要交过路费的,并且行走方向必须是靠近终点的方向。
往下走一次只能走一格,往右走一次可以走一格也可以走到当前列数的倍数格。
比如当前格子是(x,y),那么可以移动到(x+1,y),(x,y+1)或者(x,y*k),其中k>1。
qwb希望找到一种走法,使得到达右下角时他能够有最多的小钱钱。
你能帮助他吗?
Input
第一行是测试例数量 T (T<=100),接下来是T组测试数据。
每组测试数据的第一行是两个整数n,m,分别表示行数和列数(1<=n<=20,1<=m<=10000);
接下去给你一个n*m的矩阵,每个格子里有一个数字 k (-100<=k<=100)代表小钱钱的数量。 ∑nm<=3,000,000
Output
每组数据一行,输出L先生能够获得小钱钱的最大值(可能为负数)。
这个题一看dp就出来了,然后就是超时。因为我写的dp[i][j]:到(i,j)点时最多的小钱钱,然后它的状态更新从前面更新的状态中找,因为可能是前面跳过来的,然后就枚举k,就会超时。以上是填表法,根据之前会影响它的状态来更新它。但是这个之前的状态不是很好找,要靠枚举。所以我们用刷表法,意思是更新被当前点影响的点。
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 10000+10;
#define inf 3e7
int M[25][maxn];
int dp[25][maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d %d",&n,&m);
memset(M,0,sizeof(M));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&M[i][j]);
dp[i][j]=-inf;
}
}
dp[1][1]=M[1][1];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=2*j;k<=m;k+=j)
dp[i][k]=max(dp[i][k],dp[i][j]+M[i][k]);
dp[i+1][j]=max(dp[i+1][j],dp[i][j]+M[i+1][j]);
dp[i][j+1]=max(dp[i][j+1],dp[i][j]+M[i][j+1]);
}
}
printf("%d\n",dp[n][m]);
}
return 0;
}
Problem D: qwb与神奇的序列
qwb又遇到了一道题目:
有一个序列,初始时只有两个数x和y,之后每次操作时,在原序列的任意两个相邻数之间插入这两个数的和,得到新序列。举例说明:
初始:1 2
操作1次:1 3 2
操作2次:1 4 3 5 2
……
请问在操作n次之后,得到的序列的所有数之和是多少?
多组测试数据,处理到文件结束(测试例数量<=50000)。
输入为一行三个整数x,y,n,相邻两个数之间用单个空格隔开。(0 <= x <= 1e10, 0 <= y <= 1e10, 1 < n <= 1e10)。
Output
对于每个测试例,输出一个整数,占一行,即最终序列中所有数之和。
如果和超过1e8,则输出低8位。(前导0不输出,直接理解成%1e8)
这个题先推出一个式子:X(n)=3*X(n-1)-(y-x);
然后发现我们就是要求(3^n+1)*(y-x)/2的值。用快速幂就好了。其中有一点,因为我们要模上一个值嘛,模上一个值后除2,和本来的值/2再模,答案是不是一样的呢,不一样的。可以用这个公式转化..因为有递推公式,我们可以用矩阵快速幂直接计算,对了,因为矩阵中间有一个数是负数,我们之后还要加上一个mod再mod一次
x/k%z=x%(k*z)/k;
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 2e8;
long long n;
long long quickmi()
{
long long ans=1;
long long q=3;
while(n>0)
{
if(n&1)
ans=ans*q%maxn;
q=q*q%maxn;
n=n/2;
}
return ans;
}
int main()
{
long long x,y;
while(scanf("%lld %lld %lld",&x,&y,&n)!=EOF)
{
printf("%lld\n",(((quickmi()+1)/2)*(x+y)%(maxn/2))%(maxn/2));
}
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
const int inf = 1e8;
struct matrix
{
long long mat[10][10];
matrix()
{
memset(mat,0,sizeof(mat));
}
};
matrix mul(matrix A,matrix B)
{
matrix C;
for(int i=1;i<=2;i++)
{
for(int j=1;j<=2;j++)
{
for(int k=1;k<=2;k++)
{
C.mat[i][j]=(C.mat[i][j]+(A.mat[i][k]*B.mat[k][j])%inf)%inf;
}
}
}
return C;
}
matrix quickmi(matrix A,long long n)
{
matrix B;
B.mat[1][1]=B.mat[2][2]=1;
while(n>0)
{
if(n&1) B=mul(A,B);
A=mul(A,A);
n=n/2;
}
return B;
}
int main()
{
long long x,y,n;
while(scanf("%lld %lld %lld",&x,&y,&n)!=EOF)
{
matrix A;
A.mat[1][1]=3;
A.mat[1][2]=-1;
A.mat[2][1]=0;
A.mat[2][2]=1;
A=quickmi(A,n);
long long c=(x+y)%inf;
long long ans=(A.mat[1][1]+A.mat[1][2])*c%inf;
ans=(ans+inf)%inf;
printf("%lld\n",ans);
}
return 0;
}
Problem F: nimm博弈
G:
某一天,qwb去WCfun面试,面试官问了他一个问题:把一个正整数n拆分成若干个正整数的和,请求出这些数乘积的最大值。
qwb比较猥琐,借故上厕所偷偷上网求助,聪明的你能帮助他吗?
第一行为一个正整数T.(T<=100000)
接下来T行,每行一个正整数n(n<=1e9),意义如题目所述。
每一行输出一个整数,表示乘积的最大值,由于答案可能很大,请将答案对109+7取模后输出。
这个题尽量找3,然后对于前面几个数要特判
#include
#include
#include
#include
#include
using namespace std;
const int inf=1e9+7;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
int k=n/3;
int h=n-3*k;
if(h==1&&k>=1) {k--;h=4;}
long long ans=h,q=3;
if(h==0) ans=1;
while(k>0)
{
if(k&1)
ans=(ans*q)%inf;
q=(q*q)%inf;
k>>=1;
}
printf("%lld\n",ans%inf);
}
return 0;
}
K:
qwb遇到了一个问题:将分数a/b化为小数后,小数点后第n位的数字是多少?
做了那么多题,我已经不指望你能够帮上他了。。。
多组测试数据,处理到文件结束。(测试数据<=100000组)
每组测试例包含三个整数a,b,n,相邻两个数之间用单个空格隔开,其中0 <= a <1e9,0 < b < 1e9,1 <= n < 1e9。
还是快速幂
#include
#include
#include
#include
#include
using namespace std;
int main()
{
long long a,b,n;
while(scanf("%lld %lld %lld",&a,&b,&n)!=EOF)
{
long long ans=a%b,q=10%b;
n--;
while(n>0)
{
if(n&1) ans=(ans*q)%b;
q=(q*q)%b;
n=n/2;
}
printf("%lld\n",(ans*10)/b);
}
return 0;
}