之江决赛补题

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;
}

你可能感兴趣的:(contests)