2020牛客寒假算法基础集训营1 解题报告 Apare_xzc

2020牛客寒假算法基础集训营1 解题报告 Apare_xzc


2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第1张图片


比赛链接:2020牛客寒假算法基础集训营1<–

2020-02-04 13:00:00 至 2020-02-04 18:00:00
时长: 5小时
已有3952人报名


官方题解:【题解】牛客寒假集训营第一场 <–


A. honoka和格点三角形

题目链接

2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第2张图片
2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第3张图片

分析:

          求格点内能连成的面积为1的三角形的个数,要求三角形至少有一条边与x轴或y轴平行。
          满足条件的三角形的总数ans = 有一条边与x轴平行的三角形个数 ans1 有一条边与y轴平行的三角形的个数 ans2 - 直角三角形的个数ans3
          计算的方法都相同,以平行于x轴的三角形为例。三角形面积为1,一定是底为1高为2,或者底为2,高为1。那么必然有两个点的纵坐标相同,他们在纵坐标一定的时候,横坐标的范围是1到m, 于是底为1的两点有m-1种取法,每种取法可以改变纵坐标,上下移动,可知横坐标确定了的有一边平行于x轴的三角形第三个点的纵坐标有(n-2)*2种,纵坐标确定之后,三角形的高确定,第三个点有m中取法(直角三角形有2种)。
其它的情况都类似,互换m,n即可。具体公式见代码。

我的代码:
#include 
using namespace std;
#define LL long long
LL f(LL m,LL n)
{
    return ((m-2)*m%mod*(n-1)%mod*2%mod (m-1)*m%mod*(n-2)%mod*2%mod )%mod;
}
LL calRT(LL m,LL n)
{
    return (  (m-2)*2%mod*(n-1)%mod*2%mod       (m-1)*2%mod*(n-2)%mod*2%mod )%mod;
}
int main()
{
    LL m,n,ans;
    while(cin>>m>>n)
    {
        ans = (f(m,n) f(n,m))%mod;
        ans = ((ans - calRT(m,n)) % mod   mod)%mod;
        cout<<ans<<endl;
    }
    return 0;  
}

2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第4张图片


B. kotori和bangdream

题目链接

2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第5张图片

分析:

    签到题,期望 = 1.0xa/100 1.0b(100-x)/100*n

代码
#include 
using namespace std;
int main()
{
    int n,x,a,b;
    cin>>n>>x>>a>>b;
    double ans = 1.0*x*a/100 1.0*b*(100-x)/100;
    ans *= n;
    printf("%.2f\n",ans); 
    return 0;  
}

C. umi和弓道

题目链接 2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第6张图片不想写计算几何,这题没写,跳过吧…

D. hanayo和米饭

题目链接 2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第7张图片
分析:

        1到n的数列少了一个,给n-1个,求少的那一个。直接 (1 n)*n/2 - sum(arr) 即可。用n个数的和减去n-1个数的和

#include 
using namespace std;
int main()
{
    int n,x;
    cin>>n;
    LL sum = 1ll*(1 n)*n/2;
    for(int i=1;i<n;  i)
        scanf("%d",&x),sum -= x;
    cout<<sum<<endl
    return 0;  
}

E. rin和快速迭代

题目链接2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第8张图片
分析:

        也没有找到什么规律或者公式。那就按题意模拟吧。我们只需要进行迭代,每次用f(x)代替x,直到x变为2。求迭代了多少次。如何求x因数的个数呢?我们先对x进行分解质因数,得到

x = P1^C1 * P2^C2 * ... * Pk^Ck
其中P1,P2...Pk均为质数

那么每个质因数Pi的次数可以选{0,1,2…Ci},共有Ci 1种选法,那么根据乘法原理,因数的个数就是Ci 1的乘积。
我们可以先筛出1E6之内的质数,加快分解因式的速度。
我用的是欧拉线性筛,复杂度为O(n), 分解质因数的复杂度大约为根号n

我的代码
#include 
using namespace std;
#define LL long long
bool notPrime[maxn];
int sushu[maxn],cnt;//cnt表示素数的个数
void getPrime(int n); //欧拉筛法线性筛素数
LL getNumFac(LL x); //获得x因子的个数
int main()
{
    getPrime(1000000 10);
    LL x;
    while(cin>>x)
    {
        int ans = 0;
        while(x!=2)
        {
            x = getNumFac(x);
              ans;
        }
        cout<<ans<<endl;
    }
    return 0;  
}
void getPrime(int n)
{
    cnt = 0;
    for(int i=2;i<=n;  i)
    {
        if(!notPrime[i]) sushu[cnt  ] = i;
        for(int j=0;j<cnt&&1ll*i*sushu[j]<=n;  j)
        {
            notPrime[i*sushu[j]] = true;
            if(i%sushu[j]==0) break;
        }
    }
}
LL getNumFac(LL x)
{
    LL res = 1;
    for(int i=0;i<cnt&&sushu[i]<=x/sushu[i];  i)
    {
        int p = sushu[i];
        if(x%p) continue;
        int t = 0;
        while(x%p==0)   t, x/=p;
        res *= (t 1);
    }
    if(x>1) res *= 2;
    return res;
}

F. maki和tree

题目链接 2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第9张图片2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第10张图片
分析:

        求树上两点间路径只有一个黑色节点的对数。我们只需要对每一个黑色节点dfs,求出包含该黑色节点的路径数(点的对数)相加即可。
        每个黑节点有若干个白色节点与之相连,相互不经过黑色节点可达的白色节点看成是一个连通的整体,我们dfs求出这些连通的整体中白色节点的个数,记录这些个数v0,v1,v2… 可以存在一个vector之中。那么,如果黑色节点是点对两点中的某一点,路径的数目为v0,v1,v2…之和,也就是黑色节点不经过其它黑色节点可达的白色节点的个数之和。如果黑色节点不是点对中的某一点,那么两个点一定在不同的连通整体中,这样的路径数位v0,v1,v2… 这些数两两相乘相加之和。

我的代码:
#include 
using namespace std;
const int maxn = 3e5 100;
#define LL long long
#define Mst(a,b) memset(a,(b),sizeof(a))
char color[maxn];
struct Node{
    int to,Next;
}node[maxn*2];
int tot,head[maxn];
void initEdge()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}
bool vis[maxn];
void addedge(int u,int v)
{
    node[tot].to = v;
    node[tot].Next = head[u];
    head[u] = tot  ;
}
LL DFS(int x)
{
    if(color[x]=='B') return 0;
    vis[x] = true;
    LL sum = 1;
    for(int i=head[x];i!=-1;i=node[i].Next)
    {
        int to = node[i].to;
        if(color[to]=='B'||vis[to]) continue;
        sum  = DFS(to);
    }      
    vis[x] = false;
    return sum;
}
int main()
{
//  freopen("in.txt","r",stdin);
    int n,u,v;
    scanf("%d%s",&n,color 1);
    initEdge();
    for(int i=1;i<n;  i)
    {
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
    }
    LL ans = 0;
    LL cc = 0;
    for(int i=1;i<=n;  i)
    {
        if(color[i]=='B') //
        {
            vector<LL> v;
            for(int t=head[i];t!=-1;t=node[t].Next)
            {
                int to = node[t].to;
                if(color[to]=='W')
                {
                    cc = DFS(node[t].to);
                    v.push_back(cc);
                }
            }
            for(auto z:v) ans =z;
            if((int)v.size()<2) continue;
            int len = v.size();
            for(int ii=0;ii<len;  ii)
            {
                for(int jj=ii 1;jj<len;  jj)
                {
                    ans  = v[ii]*v[jj];
                }
            }
        }
    }
    cout<<ans<<endl;
    return 0;  
}

G. eli和字符串

题目链接 2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第11张图片
分析:

        我们可以每个小写字母开一个vector,记录该字母在字符串中出现的所有位置,是一个递增的序列。然后我们每个字母从v[i]开始,v[i k-1]结束的这一段含有k个相同的字母,用v[i k-1] - v[i] 1更新答案即可

我的代码:
#include 
using namespace std;
const int maxn = 1e6 100;
char str[maxn];
int main()
{
	int n,k;
	cin>>n>>k;
	scanf("%s",str);
	vector<int> v[320];
	for(int i=0;i<n;  i)
	{
		v[(int)str[i]].push_back(i);
	}
	int ans = n 100;
	for(int i='a';i<='z';  i)
	{
		int len = v[i].size();
		if(len<k) continue;
		for(int j=0;j k-1<len;  j)
		{
			ans = min(ans,v[i][j k-1]-v[i][j] 1);
		}
	}
	if(ans==n 100) ans = -1;
	cout<<ans<<endl;
	
	return 0;	
} 

H. nozomi和字符串

题目链接2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第12张图片
分析:

        在K次操作之内,求最大的数字相同的子串的长度。数字都相同,无非只有两种情况,一种是全部为0,一种是全部为1。
        我们可以二分答案,枚举子串的起点,二分子串的长度。如果是全1串,我们需要求得串中0的个数,来判断是否能在K次操作之内都变为1。我们可以维护一个0的个数的前缀和(1同理) ,于是我们可以O(1)check。起点一定的话,0或1的个数也随着长度的增加单调非递减,满足二分的性质。

我的代码:
#include 
using namespace std;
const int maxn = 1e6 100;
char str[maxn]; 
int sum0[maxn],sum1[maxn];
int main()
{
	int n,k;
	cin>>n>>k;
	scanf("%s",str 1);
	for(int i=1;i<=n;  i)
	{
		sum1[i] = sum1[i-1];
		sum0[i] = sum0[i-1]; 
		if(str[i]=='1')   sum1[i];
		else   sum0[i]; 
	}
	int ans = 0;
	int left,mid,right;
	for(int i=1;i ans-1<=n;  i)
	{
		left = i,right = n 1;
		while(right-left>1)
		{
			mid = (left right)>>1;
			if(sum0[mid]-sum0[i-1]<=k) left = mid;
			else right = mid;  
		}
		ans = max(ans,left-i 1);
	}
	for(int i=1;i ans-1<=n;  i)
	{
		left = i,right = n 1;
		while(right-left>1)
		{
			mid = (left right)>>1;
			if(sum1[mid]-sum1[i-1]<=k) left=mid;
			else right = mid;
		}
		ans = max(ans,left-i 1);
	}
	cout<<ans<<endl
	return 0;	
} 

I. nico和niconiconi

题目链接2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第13张图片
分析:

        简单dp,官方题解说的很明白
2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第14张图片

我的代码:
#include 
using namespace std;
const int maxn = 3e5 100;
#define LL long long
char str[maxn];
LL n,a,b,c; 
char A[] = "nico";
char B[] = "niconi";
char C[] = "niconiconi"; 
bool equalA(int);
bool equalB(int);
bool equalC(int);
LL dp[maxn];
int main()
{
	scanf("%lld%lld%lld%lld",&n,&a,&b,&c);
	scanf("%s",str 1);
	for(int i=1;i<=n;  i)
	{
		dp[i] = dp[i-1];
		if(equalA(i-3)) dp[i] = max(dp[i],dp[i-4] a);
		if(equalB(i-5)) dp[i] = max(dp[i],dp[i-6] b);
		if(equalC(i-9)) dp[i] = max(dp[i],dp[i-10] c);
	}
	cout<<dp[n]<<endl;
	
	return 0;	
} 
bool equalA(int st)
{
	if(st<1) return false;
	for(int i=0;i<4;  i)
		if(str[st i]!=A[i]) return false;
	return true;
}
bool equalB(int st)
{
	if(st<1) return false;
	for(int i=0;i<6;  i)
		if(str[st i]!=B[i]) return false;
	return true;
}
bool equalC(int st)
{
	if(st<1) return false;
	for(int i=0;i<10;  i)
		if(str[st i]!=C[i]) return false;
	return true;
}

J. u’s的影响力

题目链接2020牛客寒假算法基础集训营1 解题报告 Apare_xzc_第15张图片
分析:

        如果n<=2,直接输出x或y,如果n>=3,可以发现x和y的指数都是斐波那契数列的某一项,a^b的系数是也是某一项-1
Pab[i] = Pab[i-1] Pab[i-2] 1,一开始我直接退出一个三乘三的系数矩阵

// x[i 2] = x[i 1] x[i] 1
//    |x[i 2]|     |1 1 1|   |x[i 1]|  0 x2              X[n 1]
//    |x[i 1]|  =  |1 0 0| * |x[i 0]|  0 x1 -- n-1次 --> X[n]
//	  |    1 |     |0 0 1|   |    1 |  1

后来发现就是Feibonaci(n 1)-1
x的系数为Fei(n-1), y的系数为Fei(n) 如果像下面这样定义的话

//	id   1  2  3  4  5  6  7   8    9 
  //Fei  0  1  1  2  3  5  8   13  21
	//x  1  0  1  1  2  3  5   8   13  Fei(n-1)
	//y  0  1  1  2  3  5  8  13   21  Fei(n  )
//a^(b)  0  0  1  2  4  7 12  20   33  Fei(n 1)-1 

我们可以用费马小定理,计算Fei[n]的时候用1000000007的欧拉函数作为模数。由于1E9 7是质数,质数的欧拉函数等于它自己减一,所以求Fei的时候对1E9 6取模即可。
由于x,y,a^b这三个底数可能是1E9 7的倍数,结果一定为0,要注意一下。
如果指数对1000000006取模后变为0,那么x^0 返回1,就会出错,所以建议快速幂这么写:

#define LL long long
LL fast_pow(LL a,LL b,LL mod)
{
	if(a==0) return 0; //加一个特判以防万一 
	LL ans = 1;
	while(b)
	{
		if(b&1) ans = ans * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}

求斐波那契额数列的第n项可以用矩阵快速幂,系数矩阵如下:

//	   fei[n  ] =  |1  1|^n-2    |fei[2]| 1
//	   fei[n-1]    |0  1|     *  |fei[1]| 0
我的代码:
#include 
#define LL long long
using namespace std;
const int N = 3;
const int mod = 1e9 7;
LL fast_pow(LL a,LL b,LL mod);
void Mul_Matrix(LL a[N][N],LL b[N][N],int n,int mod);
void Matrix_fast_pow(LL a[N][N],LL b,int n,int mod);
LL getFeiboNa(LL n,int mod);
int main()
{
	LL a,b,x,y,n;
	cin>>n>>x>>y>>a>>b;
	a %= mod;
	x %= mod;
	y %= mod;
	if(n==1)
	{
		cout<<x<<endl;return 0;
	}
	if(n==2)
	{
		cout<<y<<endl;return 0;
	}
	a = fast_pow(a,b,mod);
	LL pa,px,py,ans;
	pa = (getFeiboNa(n 1,mod-1)-1 mod-1)%(mod-1);
	ans = fast_pow(a,pa,mod);
	px =  getFeiboNa(n-1,mod-1);
	py =  getFeiboNa(n,mod-1);
	ans = ans * fast_pow(x,px,mod) % mod * fast_pow(y,py,mod) % mod;
	printf("%lld\n",ans);
	
	return 0;	
} 
LL fast_pow(LL a,LL b,LL mod)
{
	if(a==0) return 0; 
	LL ans = 1;
	while(b)
	{
		if(b&1) ans = ans * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
void Mul_Matrix(LL a[N][N],LL b[N][N],int n,int mod)
{
	LL c[N][N] = {0};
	for(int i=0;i<n;  i)
		for(int j=0;j<n;  j)
			for(int k=0;k<n;  k)
				c[i][j] = (c[i][j] a[i][k]*b[k][j]%mod)%mod;
	for(int i=0;i<n;  i)
		for(int j=0;j<n;  j)
			a[i][j] = c[i][j];
}
void Matrix_fast_pow(LL a[N][N],LL b,int n,int mod)
{
	LL ans[N][N] = {0};
	for(int i=0;i<n;  i)
		ans[i][i] = 1;
	while(b)
	{
		if(b&1) Mul_Matrix(ans,a,n,mod);
		Mul_Matrix(a,a,n,mod);
		b >>= 1;
	}
	for(int i=0;i<n;  i)
		for(int j=0;j<n;  j)
			a[i][j] = ans[i][j];
}
LL getFeiboNa(LL n,int mod)
{
	if(n<=2) return n-1;
	LL m2[N][N] = {
		1,1,666,
	    1,0,999,
	    5,20,1314,
	};
	Matrix_fast_pow(m2,n-1,2,mod);
	return m2[1][0];	
}

xzc
2020.2.6 1:08


你可能感兴趣的:(解题报告,牛客寒假训练营,Apare_xzc)