zoukankan      html  css  js  c++  java
  • HIT2021五一集训(一)

    2020第十五届黑龙江省赛的题目,怪不得眼熟
    (状态很不好

    A. August

    Gym 102803A

    题意:微积分结论题。(ans=pi a^2+4ab)。其中,可以使用(pi=acos(-1))来求(pi)
    具体过程:显然图形在x轴上方的部分的面积相当于以a为半径的圆形面积。
    现在分析图形在第三象限部分的面积。

    (y=frac{2b}{pi}(arccos(frac{x+a}{a})-pi))

    所以,(x=a*cos(frac{2b}{pi}y+pi)-a)

    (S=-egin{matrix} int_{-2b}^{0} (a*cos(frac{2b}{pi}y+pi)-a)\, dy end{matrix})

    (S=-aegin{matrix} int_{-2b}^{0} cos(frac{2b}{pi}y+pi)\, dy+2ab end{matrix})

    (令z=frac{2b}{pi}y,S=-frac{api}{2b}egin{matrix} int_{-pi}^{0} cos(z+pi)\, dz+2ab end{matrix})

    (S=-frac{api}{2b}*(sin(pi)-sin(0))+2ab=2ab)

    所以图形在x轴下方的面积为(2*2ab=4ab)
    加上上半部分即(pi a^2+4ab)
    (应该没有推错的地方吧,一百年没做微积分了

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const double PI=acos(-1);
    int main()
    {
    	int T;
    	double a,b,ans;
    	scanf("%d",&T);
    	while (T--)
    	{
    		scanf("%lf%lf",&a,&b);
    		ans=PI*a*a+4.0*a*b;
    		printf("%.8lf
    ",ans);
    	} 
    	return 0;
    }
    

    C. Cornelia Street

    Gym 102803C

    题意:给你一个串S,已知S=AA...ABB...BAA...Aa,其中|A|=|B|,A!=B,a为A的真前缀,即满足0<=|a|<|A|,问最短的A,B

    思路:看上去很难。(可能是因为我脑子不大好使)。我甚至用了kmp(刚开始想歪的结果,实际上好像并不需要)

    枚举A的长度,然后不断检查下一段字符串是否与A相同;若不同,则认为读到了字符串B,然后不断检查下一段字符串是否与B相同;若不同,则比对这段字符串与A,若还是不同,则退出进行下一次枚举;否则继续不断检查下一段字符串是否与A相同。

    (所以就是个枚举题,我为啥会想到一些七七八八的东西呢。。。

    代码懒得改了,并不需要用kmp(

    说起来这个|S|<8e5,居然能过。。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int maxn=1e6+5;
    char s[maxn];
    int p[maxn];
    void prework(char b[],int len)
    {
    	p[0]=-1;
    	int i,j=-1;
    	for (i=1;i<len;i++)
    	{
    		while (j>=0 && b[j+1]!=b[i]) j=p[j];
    		if (b[j+1]==b[i]) j++;
    		p[i]=j;
    	}
    }
    int main()
    {
    	int i,j,k,flag,be,n,T,ans,len,Aa;
    	scanf("%s",s);
    	len=strlen(s);
    	prework(s,len);
    	//for (i=0;i<len;i++) printf("%d ",p[i]);
    	//printf("
    ");
    	
    	Aa=p[len-1];
    	//printf("len:%d %d
    ",len,Aa);
    	for (i=2;i<=Aa+1;i++)//枚举A长度 
    	{
    		flag=0;
    		for (j=i;j<len;j+=i)
    		{
    			//printf("ij %d %d %d %c
    ",i,j,flag,s[j]);
    			if (flag==0)
    			{
    				for (k=0;k<i;k++)
    				{
    					if (j+k>=len ||s[k]!=s[j+k])
    					{
    						flag=1;break;//读到B串了 
    					} 
    				}
    				if (flag==1)
    				{
    					//if (k==0) 
    					be=j;
    					//else break;//失败 
    				}
    			}
    			else if (flag==1)
    			{
    				for (k=0;k<i;k++)
    				{
    					if (j+k>=len || s[be+k]!=s[j+k])
    					{
    						flag=2;break;//读到后面的A串了 
    					}
    				}
    				if (flag==2)
    				{
    					for (k=0;k<i;k++)
    					{
    						if (j+k>=len ||s[k]!=s[j+k])
    						{
    							flag=0;break;//这不是A串 
    						} 
    					}
    					if (flag==0) break; 
    				}
    			}
    			else if (flag==2)
    			{
    				for (k=0;k<i;k++)
    				{
    					if (j+k>=len) break;
    					if (s[k]!=s[j+k])
    					{
    						flag=0;break;
    					}
    				}
    				if (flag==0) break;//失败
    			}
    			//printf("ij %d %d %d %c
    ",i,j,flag,s[i]);
    		}
    		if (flag==2)
    		{
    			for (j=0;j<i;j++) printf("%c",s[j]);
    			printf(" ");
    			for (j=0;j<i;j++) printf("%c",s[be+j]);
    			printf("
    ");
    			break;
    		}
    	}
    	return 0;
    }
    

    F. False God

    Gym 102803F

    题意:给一个棋盘,棋盘里有一个Kin棋和n个Fu棋。
    若当前Kin棋在(x,y),那么它可以移动一步,到(x+1,y),(x+1,y+1),(x,y+1),(x−1,y+1),(x−1,y),(x,y−1);若当前Fu棋在(x,y),那么它可以移动一步,到(x,y-1)。
    现在Kin棋先手,当Kin棋移动一步后,每一个Fu棋都会移动一步。
    若任意棋移动后,Kin与Fu重合,那么算做Kin吃掉了Fu。
    现在告诉你棋盘的情况,问Kin最多能吃掉多少个Fu。
    思路:Fu棋的运动是固定的,显然可以发现,除了Fu在Kin的下方一格的情况,所有y坐标小于Kin的y坐标的Fu,Kin都不能吃到。所以我们可以删掉这些Fu不做考虑。
    假设当前Kin在(e[i].x,e[i].y),Fu在(e[j].x,e[j].y),那么,如果|e[i].x-e[j].x|<=|e[i].y-e[j].y|+1,那么Kin就能够吃到这个Fu。
    (于是开始时我就按照这个公式,在满足条件的两个Fu(或者一个Fu和一个Kin)之间连边,bfs跑最长路,然后发现,这样会出现重复经过一个点的情况。。。需要记录当前每个点的访问情况,然而n<1e3,看上去是无法用bfs解决的。。。然后又想到用dfs,然后很显然地TLE了。
    (参考了一下别人的想法之后)对于三个点1,2,3,若它们之间都有边,且y1<y2<y3,从1开始计算,显然先考虑2是比较合理的,因为Kin相对于Fu无法下移(即它们的(Delta y)无法变小)。
    于是将所有的Fu按照y坐标从小到大排序。记f[]为Kin(0)到达Fu(i)时吃掉的Fu的数量,初始化f[0]为0表示Kin在初始位置,其余的f均设为负无穷。
    对于一个Fu(i),用y坐标小于它且满足前文的公式的Kin或Fu(j)更新答案,即f[i]=max(f[i],f[j]+1)。
    然后f[]的最大值(还要加上处于初始Kin下方一格的Fu)就是答案。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    using namespace std;
    const int maxn=1005;
    const int inf=2e9;
    int f[maxn];
    struct node
    {
    	int x,y;
    }e[maxn];
    bool cmp(node a,node b)
    {
    	if (a.y==b.y) return a.x<b.x;
    	return a.y<b.y;
    } 
    int main()
    {
    	int i,j,T,n,m,x,y,dx,dy,add,ans;
    	scanf("%d",&T);
    	while (T--)
    	{
    		m=0;ans=0;add=0;
    		scanf("%d%d%d",&e[0].x,&e[0].y,&n);
    		for (i=1;i<=n;i++) 
    		{
    			scanf("%d%d",&x,&y);
    			if (e[0].x==x && e[0].y-1==y) add++;
    			else if (y>=e[0].y)
    			{
    				e[++m].x=x;
    				e[m].y=y;
    				f[m]=-inf;
    			}
    		}
    		sort(e+1,e+m+1,cmp);
    		f[0]=0;
    		for (i=1;i<=m;i++)
    		{
    			for (j=0;j<i;j++)
    			{
    				dx=e[i].x-e[j].x;
    				if (dx<0) dx=-dx;
    				dy=e[i].y-e[j].y;
    				if (dx<=dy+1)
    				{
    					f[i]=max(f[i],f[j]+1);
    				}
    			}
    			ans=max(ans,f[i]);
    		}
    		ans+=add;
    		printf("%d
    ",ans);
    	}
    	return 0; 
    }
    

    G. Goodbye

    Gym 102803G

    题意:一种游戏,两名玩家轮流取数。第一轮的第一个取数的玩家可以取一个n的真因子d,(真因子就是不为1且不为本身的因子),接下来下一个玩家将取一个上一个玩家取的数的真因子,比如对于第一轮第二个取数的玩家,就要取d的一个真因子。如此轮流进行,直至有一位玩家无法再取出一个真因子时,该玩家获胜。问在先手获胜的条件下,先手在第一轮能够取的最大数是什么。

    思路:乍一看像博弈论,然而并不是。仔细分析只有三种情况。

    1、存在a,b满足n%(ab)==0,并且ab!=n,其中a,b均为n的真质数因子。(或者换一种表述,n可以分解为两个以上质数的乘积。)此时先手取走a*b,那么后手只能取a或b,先手必胜。

    2、n等于两个质数的乘积,即n=a*b。此时先手只能取a或b,后手必胜。

    3、n为质数,先手直接获胜。

    第2种情况直接输出-1,第3种情况直接输出0。而对于第1种情况,只需在n的所有真质数因子中找到最大的两个,它们的乘积就是先手能取的最大数。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int maxn=1e5+5;
    int ip[maxn],pr[maxn],cnt;
    void pre()
    {
    	int i,j;
    	for (i=2;i<=1e5;i++)
    	{
    		if (!ip[i]) pr[++cnt]=i;
    		for (j=1;j<=cnt && pr[j]*i<=1e5;j++)
    		{
    			ip[pr[j]*i]=1;
    			if (i%pr[j]==0) break;
    		}
    	}
    }
    int work(int n)
    {
    	int i,re=1,now=0;
    	for (i=cnt;i>=1;i--)
    	{
    	    if (pr[i]>n) continue; 
    		while (n%pr[i]==0) 
    		{
    			//printf("n %d %d
    ",n,pr[i]);
    			if (!now) re=pr[i],now=1;
    			else 
    			{
    				return re*pr[i];
    			}
    			n=n/pr[i];
    		}
    	} 
    	return 0;
    }
    int main()
    {
    	int i,n,T,ans,nans;
    	pre();
    	//for (i=1;i<=cnt;i++) printf("%d
    ",pr[i]);
    	scanf("%d",&T);
    	while (T--)
    	{
    		scanf("%d",&n);
    		ans=work(n);
    		//printf("ans:%d
    ",ans);
    		if (ans==0)
    		{
    			printf("0
    ");
    		}
    		else 
    		{
    			if (ans!=n) printf("%d
    ",ans);
    			else printf("-1
    ");
    		}
    	} 
    	return 0;
    }
    

    H. Hate That You Know Me

    Gym 102803H

    题意:定义 (sigma_k(n)=sum_{d|n}^{}{d^k})
    给出a,b,n,求(left(left(sum_{i=1}^n{sigma_a(i)} ight) oplus left(sum_{i=1}^n{sigma_b(i)} ight) ight)quad mod quad 2^{64})
    其中 (oplus)表示按位异或。

    思路:整除分块题。就是这个取模有点难搞。。。搞半天不过只好参考别人的写法了。

    关于数论分块问题,大概就是,求解类似于(sum_{i=1}^{n}{lfloor frac{n}{i} floor})的式子时,由于(lfloor frac{n}{i} floor)单调不增,所以存在多个区间([l,r]),使得对于(forall i,jin[l,r]),都有(lfloor frac{n}{i} floor)=(lfloor frac{n}{j} floor)成立。
    对于任意一个(i),满足上式的最大的(j)可由(j=igg lfloor frac{n}{ ig lfloor frac{n}{i} ig floor} igg floor)求出。
    根据以上的结论,我们可以把([1,n])分成个区间(块)。
    对于([l,r])区间,它对答案的贡献为({lfloor frac{n}{l} floor}*(r-l+1))

    然后回到这道题,分析后可以发现(sum_{i=1}^n{sigma_k(i)}=sum_{i=1}^n{i^k lfloor frac{n}{i} floor})
    而题目中所给的a,b有范围(0le a,b<4),即只有0,1,2,3四种取值。

    1至n的0次方之和即为n,

    1次方和即为(frac{n(n+1)}{2})

    平方和有公式(frac{n(n+1)(2n+1)}{6})

    立方和有公式([frac{n(n+1)}{2}]^2)

    于是对于每一段([l,r]),根据(l至r的k次方和)*(r-l+1)统计答案,就可求出(sum_{i=1}^n{sigma_k(i)})

    由于mod (2^{64}),使用unsigned long long。

    (关于数论分块,顺便贴一个网上找的笔记

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef unsigned long long ull;
    ull npow(ull n)
    {
    	ull a=n,b=n+1;
    	if (a&1) b>>=1;
    	else a>>=1;
    	ull re=a*b;
    	return re;
    }
    ull nsquare(ull n)
    {
    	ull a=n,b=n+1,c=2*n+1;
    	if (a&1) b>>=1;
    	else a>>=1; 
    	if (a%3==0) a/=3;
    	else if (b%3==0) b/=3;
    	else c/=3;
    	ull re=a*b*c;
    	return re;
    }
    ull ncube(ull n)
    {
    	ull a=n,b=n+1;
    	if (a&1) b>>=1;
    	else a>>=1; 
    	ull re=a*b;
    	re=re*re;
    	return re;
    }
    ull block(ull op,ull n)
    {
    	ull l,r,re,ans=0;
    	for (l=1;l<=n;l=r+1)
    	{
    		r=n/(n/l);
    		if (r>n) r=n;
    		re=(ull)(n/l);
    		if (op==0) re=re*(r-l+1);
    		else if (op==1) re=re*(npow(r)-npow(l-1));
    		else if (op==2) re=re*(nsquare(r)-nsquare(l-1));
    		else if (op==3) re=re*(ncube(r)-ncube(l-1));
    		ans+=re; 
    		//printf("re %lld %lld %d %d
    ",re,ans,l,r);
    	}
    	return ans;
    }
    int main()
    {
    	ull a,b,n,ans,ansa,ansb;
    	scanf("%llu%llu%llu",&a,&b,&n);
    	ansa=block(a,n);
    	ansb=block(b,n);
    	ans=ansa^ansb;
    	//printf("%lld %lld
    ",ansa,ansb);
    	printf("%llu
    ",ans);
    	return 0;
    }
    

    L. Let's Get Married

    Gym 102803L

    题意:...懒得概括了。总之是细节题,不难但是容易出错。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int main()
    {
    	int T,op;
    	long long i,id,nx=0,ny=0,x,y;
    	long long rou,top,ans,rr,ll,dd;
    	scanf("%d",&T);
    	while (T--)
    	{
    		scanf("%d",&op);
    		if (op==1)
    		{
    			scanf("%lld",&id);
    			rou=(id-1)/2;
    			rou=sqrt(rou);
    			for (i=max(rou-1,(long long)0);;i++)
    			{
    				if (1ll*2*i*(i-1)+1>id) break;
    			}
    			rou=i-1;
    			top=2*rou*(rou-1)+1;//最上 
    			rr=top+2*rou-1;//最右 
    			dd=top+3*rou-1;//最下
    			ll=top+4*rou-1;//最左 
    			//printf("rou %lld %lld %lld %lld %lld
    ",rou,top,rr,dd,ll);
    			if (id==0) 
    			{
    				x=0;y=0;
    			}
    			else if (id<=rr)//上面 
    			{
    				y=rou-(id-top+1)/2;
    				if (id%2==0) x=rou-y;
    				else x=y-rou;
    			} 
    			else//下面 
    			{
    				if (id<dd)
    				{
    					y=rr-id;
    					x=rou+y;
    				}
    				else 
    				{
    					y=id-ll;
    					x=-(rou+y);
    				} 
    			} 
    			//printf("ans1: %lld %lld
    ans1: ",x,y);
    			printf("%lld %lld
    ",x-nx,y-ny);
    			nx=x;ny=y;
    		}
    		else 
    		{
    			scanf("%lld%lld",&x,&y);
    			if (x>=0 && y>=0)
    			{
    				rou=x+y;
    				top=2*rou*(rou-1)+1;//最上
    				ans=top-1; 
    				for (i=1;i<=x;i++) ans+=2;
    			}
    			else if (x>=0 && y<0)
    			{
    				rou=x-y;
    				top=2*rou*(rou-1)+1;
    				ans=top+2*rou-1;//最右 
    				for (i=rou-1;i>=x;i--) ans++;	
    			}
    			else if (x<0 && y<=0)
    			{
    				rou=-x-y;
    				top=2*rou*(rou-1)+1;
    				ans=top+3*rou-1;//最下 
    				for (i=-1;i>=x;i--) ans++;
    			} 
    			else if (x<0 && y>0)
    			{
    				rou=-x+y;
    				top=2*rou*(rou-1)+1;//最上
    				ans=top;
    				for (i=-1;i>=x;i--) ans+=2;
    			}
    			//printf("ans2: ");
    			printf("%lld
    ",ans);
    			nx=x;ny=y;
    		}
    	} 
    	return 0;
    }
    
  • 相关阅读:
    第四周学习总结
    第十三周编程总结
    2018秋季第十三周助教总结
    第十三周学习总结
    使用函数输出水仙花数 (void的用法)
    ZOJ3229 有源汇上下界最大流
    codeforces-1176 (div3)
    codeforces-1077 (div3)
    牛客假日团队赛1 题解
    牛客练习赛38 离线 启发式合并并查集
  • 原文地址:https://www.cnblogs.com/lsykk/p/14767379.html
Copyright © 2011-2022 走看看