9.2 p.m.小结

T1问题 A: 回文日期

题目描述

    在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

    牛牛习惯用8位数字表示一个日期,其中,前4位代表年份,接下来两位代表月份,最后两位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表示方法不会相同。

    牛牛认为,一个日期是回文的,当且仅当表示这个日期的8位数字是回文的。现在,牛牛想知道:在他指定的两个日期之间(包含这两个日期本身),有多少个真实存在的日期是回文的。

输入

    第一行表示牛牛指定的起始日期date1。

    第二行表示牛牛指定的终止日期date2。

    保证date1和date2都是真实存在的合法日期,且年份部分一定为4 位数字,且首位数字不为0。保证date1一定不晚于date2。

输出

    一行一个整数,表示在date1和date2之间,有多少个日期是回文的。

样例输入

20110101

20111231

样例输出

1

题解

由于数据保证年月日只能是八为,且最高位大于0.因此我们思考一共有多少天,从10000101到99991231,不超过366*10000也就是三百多万天,所以完全可以O(n)来完成本题,判断是否是回文数也很简单,就是把每一位处理出来,判断是否从左向右的第i个数字和第9-i个数字(即从右向左数的第i个数字)相同

参考代码

#include<cstdio>
using namespace std;
int a,b,ans=0,comp[100];
bool pd()
{
	return (comp[1]==comp[8])&&(comp[2]==comp[7])&&(comp[3]==comp[6])&&(comp[4]==comp[5]);
}
int main()
{
	scanf("%d%d",&a,&b);
	while(1)
	{
		int a0=a,year,month,day;
		for(int i=1;i<=8;i++) 
		{
			comp[i]=a0%10;
			a0/=10;
		}
		if(pd()) ans++; 
		year=a/10000;
		month=(a%10000)/100;
		day=a%100;
		
		if(month==1||month==3||month==5||month==7||month==8||month==10||month==12)
		{
			if(day<31) day++;
			else 
			{
				day=1;
				if(month<12)month++;
				else 
				{
					year++;
					month=1;
				}
			}
		}
		else if(month==4||month==6||month==9||month==11)
		{
			if(day<30) day++;
			else 
			{
				day=1;month++;
			}
		}
		else
		{
			if((year%4==0&&year%100!=0)||year%400==0)
			{
				if(day<29) day++;
				else 
				{
					day=1;month++;
				}
			}
			else
			{
				if(day<28) day++;
				else 
				{
					day=1;month++;
				}
			}
		}
		a=year*10000+month*100+day;
		if(a==b) break; 
	}
	printf("%d",ans);
	return 0;
}

T2问题 B: 最大公约数

题目描述

输入两个正整数x和y,输出它们的最大公约数。

输入

一行两个正整数x和y,2≤x≤y≤107,中间用一个空格隔开。

输出

一行一个正整数,表示它们的最大公约数。

样例输入

8 12

样例输出

4

题解

这道题啊,其实很难写题解的,因为辗转相除法谁不会啊~~

详情见下gcd函数

参考代码

#include<cstdio>
using namespace std;
int gcd(int m,int n)
{
	return n==0?m:gcd(n,m%n);
}
int main()
{
	int a,b;
	scanf("%d%d",&a,&b);
	printf("%d",gcd(a,b));
	return 0;
}

T3:问题 C: 校门外的树

题目描述

    学校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。可以把马路看成一个数轴,马路的-端在数轴0的位置,另一端在L的位置,数轴.上的每个整数点(即0,1,2,...,L)都种有一棵树。

    由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。请计算将这些树都移走后,马路上还有多少棵树。

输入

      第一行有两个正整数L(1≤L≤10000)和M(1 ≤M≤100),L代表马路的长度,M代表区域的数目,L和M之间用一个空格隔开。

      接下来的M行,每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点和终止点的坐标。

输出

    一行一个整数,表示马路上剩余的树的数目。

样例输入

500 3

150 300

100 200

470 471

样例输出

298

提示

    数据规模:对于10%的数据,区域之间没有重合的部分;对于其他的数据,区域之间有重合的情况。

题解

其实可以观察到,如果n小m大,那么用O(n)扫描一遍即可得到正确的ans。如果说n很大呢,那么就可以采用线段树动态开点求和的方法,以O(mlogm)的效率过这道题。这里给出第一种方法的代码以供参考(没啥技术含量的)。

参考代码

#include<cstdio>
using namespace std;
int n,m,ans=0,L[200],R[200];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&L[i],&R[i]);
	}
	for(int i=0;i<=n;i++)
	{
		int pd=0;
		for(int j=1;j<=m;j++)
		{
			if(L[j]<=i&&R[j]>=i)
			{
				pd=1;break;
			}
		}
		if(pd==0) ans++;
	}
	printf("%d",ans);
	return 0;
}

T4:问题 D: 蛇形方阵2

题目描述

输入一个正整数n,生成一个nxn的蛇形方阵(具体见样例)。

输入

一行一个正整数n,1≤n≤20。

输出

共n行,每行n个正整数,每个正整数占5列。

样例输入

5

样例输出

   15    7    6    2    1

   16   14    8    5    3

   22   17   13    9    4

   23   21   18   12   10

   25   24   20   19   11

题解

这钟蛇形矩阵是有技巧的,就是以对角线方向为基本方向,加上一堆特判,就能得到答案。我对于2*n+1个对角线,以右上角为基元,前n条对角线为一组,后(n-1)条对角线为一组,建立了两个方向转移。详情见下代码。

参考代码

#include<cstdio>
using namespace std;
int d=0,n,e_map[30][30];
int dx1[4]={-1,0,1,1};
int dy1[4]={-1,-1,1,0};
int dx2[4]={-1,1,1,0};
int dy2[4]={-1,0,1,-1};
bool pd(int x,int y)
{
	return (x>0)&&(x<=n)&&(y>0)&&(y<=n);
}
int main()
{
	scanf("%d",&n);
	e_map[1][n]=1;
	int x=1,y=n,tot=1;
	for(int i=1;i<=n;i++)
	{
		while(pd(x+dx1[d],y+dy1[d]))
		{
			x+=dx1[d];y+=dy1[d];
			e_map[x][y]=++tot;
		}
		d=(d+1)%4;
		x+=dx1[d];y+=dy1[d];
		e_map[x][y]=++tot;
		d=(d+1)%4;
		x+=dx1[d];y+=dy1[d];
		e_map[x][y]=++tot;
	}
    //处理第一部分
	if(n%2==1)
	{
		tot=e_map[2][1]=e_map[1][1]+1;
		d=2;
	}//处理过度区域
	else 
	{
		tot=e_map[n][n-1]=e_map[n][n]+1;
		d=0;
	}
    //处理第二部分
	for(int i=1;i<n;i++)
	{
		while(pd(x+dx2[d],y+dy2[d]))
		{
			x+=dx2[d];y+=dy2[d];
			e_map[x][y]=++tot;
		}
		d=(d+1)%4;
		x+=dx2[d];y+=dy2[d];
		e_map[x][y]=++tot;
		d=(d+1)%4;
		x+=dx2[d];y+=dy2[d];
		e_map[x][y]=++tot;
	}
	//按要求输出答案
	for(int i=1;i<=n;i++)
	  {
	  	for(int j=1;j<=n;j++)
	  	{
	  		if(e_map[i][j]>=0&&e_map[i][j]<10)
	  			printf("    %d",e_map[i][j]);
			else if(e_map[i][j]>=10&&e_map[i][j]<100)
	  			printf("   %d",e_map[i][j]);
	  		else if(e_map[i][j]>=100&&e_map[i][j]<1000)
	  			printf("  %d",e_map[i][j]);
		  }
	  	  if(i<n)
	  	printf("\n");
	  }
	return 0;
}

T5:问题 E: 单点修改,区间查询

题目描述

输入

第一行包含 2 个正整数 n,q,表示数列长度和询问个数。保证 1≤n,q≤106 。
第二行 n 个整数 a[1],a[2],…,a[n],表示初始数列。保证 ∣a[i]∣≤106 。
接下来 q 行,每行一个操作,为以下两种之一:
1)给定 i,x,将 a[i] 加上 x;
2)给定 l,r,求 a[l]+a[l+1]+⋯+a[r] 的值。
保证 1≤l≤r≤n, ∣x∣≤106 。

样例输入

3 2

1 2 3

1 2 0

2 1 3

样例输出

6

提示


【数据范围与提示】

对于所有数据,1≤n,q≤106 , ∣a[i]∣≤106 , 1≤l≤r≤n, ∣x∣≤106 。

题解

这道题,嘿嘿,听说用树状数组做的人都错了。还好我不辞辛苦(背~板),写了个线段树。

参考代码

#include<cstdio>
#define lc (x<<1)
#define rc (x<<1|1)
#define LL long long
#define MAXN 1000010
using namespace std;
struct tree2
{
	int l,r;
	LL sum;
}ask[MAXN*4];
int n,q;
LL a[MAXN];
void make_tree(int x,int l,int r)
{
	ask[x].l=l;ask[x].r=r;
	if(l==r)
	{
		ask[x].sum=a[l];
		return ;
	}
	int mid=(l+r)/2;
	make_tree(lc,l,mid);
	make_tree(rc,mid+1,r);
	ask[x].sum=ask[lc].sum+ask[rc].sum;
}
void update(int x,int pos,LL k)
{
	if(ask[x].l==ask[x].r)
	{
		ask[x].sum+=k;
		return;
	}
	int mid=(ask[x].l+ask[x].r)/2;
	if(mid<pos) update(rc,pos,k);
	else update(lc,pos,k);
	ask[x].sum=ask[lc].sum+ask[rc].sum;
}
LL query(int x,int l,int r)
{
	if(ask[x].l>r||ask[x].r<l) return 0;
	if(ask[x].l>=l&&ask[x].r<=r) return ask[x].sum;
	return query(lc,l,r)+query(rc,l,r);
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	make_tree(1,1,n);
	while(q--)
	{
		int pd;scanf("%d",&pd);
		if(pd==1)
		{
			int pos,x;scanf("%d%lld",&pos,&x);
			update(1,pos,x);
			
		}
		else 
		{
			int l,r;
			scanf("%d%d",&l,&r);
			printf("%lld\n",query(1,l,r));
		}
	}
	return 0;
}

T6:问题 F: 溶液模拟器

题目描述

    小谢虽然有很多溶液,们是还是没有办泌配成想要的存液,因为万一倒锴了就没有办法挽回了。因此,小谢到网上下载了一个溶液配置模拟器。模拟器在计算机中构造一种虚拟溶液,然后可以虚拟地向当前煽拟溶液中加人一定浓度、一定体积的这种游液,模拟器会快速地算出倒入后虚拟溶液的浓度和体积。当然,如果倒错了可以撤销。

    模拟器的使用步骤如下:

    1)为模拟器设置一个初始体积和浓度V0、C0%。

    2)进行一系列操作,模拟器支持两种操作:

    P(v,c)操作:表示向当前的虚拟溶液中加入体积为v浓度为c的溶液;

    乙操作:撤销上一步的P操作。

输入

      第一行两个整数,表示V0和CO,0≤C0≤100;

      第二行一个整数n,表示操作数,n≤10000;

      接下来n行,每行一条操作,格式为:P_v_c或Z。

    其中_代表一个空格,当只剩初始溶液的时候,再撤销就没有用了。

    任意时刻质量不会超过231-1。

输出

    n行,每行两个数Vi,Ci,其中Vi为整数,Ci为实数(保留5位小数)。

    其中,第i行表示第i次操作以后的溶液体积和浓度。

样例输入

100 100

2

P 100 0

Z

样例输出

200 50.00000

100 100.00000

题解

这道题是一道模拟题,知识性的东西并不多,但是要用到栈这个数据结构。注意一点:回到初始状态就不要退了,否则会爆栈。

参考代码

#include<cstdio>
using namespace std;
int n,v[10010],cnt=0;
double c[10010];
char s[5];
int main()
{
	scanf("%d%lf",&v[0],&c[0]);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s);
		if(s[0]=='P')
		{
			int vt;double ct;
			scanf("%d%lf",&vt,&ct);
			cnt++;	
			v[cnt]=v[cnt-1]+vt;
			c[cnt]=((double)v[cnt-1]*c[cnt-1]/100.0+(double)vt*ct/100.0)/(double(v[cnt]))*100.0;
		}
		else
		{
			if(cnt) cnt--;
		}
		printf("%d %.5f\n",v[cnt],c[cnt]);
	}
	return 0;
}

T7:问题 G: 转圈游戏

题目描述

n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏。按照顺时针方向给 n 个位置编号,从 0 到 n-1。最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,……,依此类推。

游戏规则如下:每一轮第 0 号位置上的小伙伴顺时针走到第 m 号位置,第 1 号位置小伙伴走到第 m+1 号位置,……,依此类推,第 n−m 号位置上的小伙伴走到第 0 号位置,第 n-m+1 号位置上的小伙伴走到第 1 号位置,……,第 n-1 号位置上的小伙伴顺时针走到第 m-1 号位置。

现在,一共进行了 10k  轮,请问 x 号小伙伴最后走到了第几号位置。

输入

输入共 1 行,包含 4 个整数 n、m、k、x,每两个整数之间用一个空格隔开。

输出

输出共 1 行,包含 1 个整数,表示 10k  轮后 x 号小伙伴所在的位置编号。

样例输入

10 3 4 5

样例输出

5

提示

【数据范围与提示】

对于 30% 的数据,0<k<7;

对于 80% 的数据,0<k<107 ;

对于 100% 的数据,1<n<106 ,0<m<n,1≤x≤n,0<k<109

题解

这一道题观察到数据范围如此巨大,自然就想到了找规律了。如何找?可以发现,无论如何,第k号小伙伴的座次号应该是一个循环号,并且可能是中断的那种,如:1,2,4,5,2,4,5,2,4,5...其实,我们可以枚举得到这个循环和开始的单独的数的个数,并且存起来。现在我们来处理10的k次方这样一个大数,首先可以想到,枚举k来得到ans,但是k很大,所以考虑快速幂来解决问题。同时我们还要用到模数的减法引理和乘法引理,就能够解决该题了。

参考代码

#include<cstdio>
#include<map>
#define LL long long
using namespace std;
map<long long,int>vis;
LL p[1000001],n,m,k,x,cnt=0;
LL qpow(LL x,LL y,LL p)
{
	LL ret=1ll;
	while(y)
	{
		if(y&1)
		ret=(ret*x)%p;
		x=x*x%p;
		y/=2ll;
	}
	return ret;
}
int main()
{
	scanf("%lld%lld%lld%lld",&n,&m,&k,&x);
	vis[x]=0,p[0]=x;
	LL dist=0,ans=1;
	while(1)
	{
		dist=vis[(p[cnt]+m)%n];
		p[++cnt]=(p[cnt-1]+m)%n;
		if(vis[p[cnt]]) 
		{
			cnt=cnt-vis[p[cnt]];
			break;
		}
		else if(p[cnt]==x)
		{
			break;
		}
		else vis[p[cnt]]=cnt;
	}
	ans=qpow(10,k,cnt);
	ans=(ans-dist%cnt+cnt)%cnt;
	printf("%d",p[ans]);
	return 0;
}

T8:问题 H: 生日

题目描述

    小林的朋友苏苏要过生日,正好小林有两张价值不菲的商场购物券,所以他决定买N件礼物送给苏苏。小林选好了N件礼物,并且它们的价格之和恰好为两张购物券的面额之和。当小林去结账时,突然发现商场对购物券的使用有非常严格的规定:一次只允许使用一张、不找零、不与现金混用。小林身上根本没有现金,并且他不愿意放弃挑选好的礼物。这就意味着,他只能通过这两张购物券结账,而且每一张购物券所购买的物品的总价格,必须精确地等于这张购物券的面额。
    怎样才能顺利地买回这N件礼物呢?本题的任务就是帮助小林确定是否存在一个购买方案。现已知其中一张购物券的面额以及所有商品的价格,只需要确定能否找到一种方案使得选出来的物品的价格总和正好是这张购物券的面额即可。

输入

    输人多组数据。
    每组数据的第一行为两个正整数N和M,分别表示一共挑选了N件物品以及一张购物券的面额为M。接下来一行,有N个用空格隔开的正整数,第i个数表示第i个物品的价格。

输出

    输出若于行,每行一个单词“YES”或者“NO”,分别代表存在一个购买方案和不存在一个购买方案。

样例输入

10 2000

1000 100 200 300 400 500 700 600 900 800

10 2290

1000 100 200 300 400 500 700 600 900 800

样例输出

YES

NO

提示


【数据规模】

    对于30%的输人文件:所有的N≤20。

    对于100%的输人文件:所有的N≤40,并且M和物品的总价值不超过231-1,测试组数不超过10组,不少于5组。

题解

这道题显然可以用搜索做。只不过2的40次方是我们无法接受的效率,因此我们将40个物品分为2部分,先搜前半部分,然后用哈希记录答案,然后再搜后半部分,看看有没有存在的ans被搜到过,然后就可以直接得到答案了。由于哈希可能会出现冲突,所以考试时建议双哈或者三哈。此处找到一种单哈方法,代码如下。

参考代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN1 100007
using namespace std;
struct tree1
{
	int to,nxt;
}tr1[2000100];
bool comp1(int p,int q){ return p>q; }
int n,m,a[50],cnt=0,head1[2000100],vis[50],ans=0,pd=0,sum[50];
void make_tree1(int u,int v)
{
	tr1[++cnt].nxt=head1[u];
	tr1[cnt].to=v;
	head1[u]=cnt;
}
void dfs(int k)
{
	if(k==n/2+1)
	{
		if(ans==0) pd=1;
		else 
		{
			make_tree1((m-ans)%MAXN1,m-ans);
			return;
		}
	}
	if(pd==1) return;
	if(ans-a[k]>=0)
	{
		vis[k]=1;
		ans-=a[k];
		dfs(k+1);
		ans+=a[k];
	}
	vis[k]=0;
	dfs(k+1);
}
void dfs1(int k)
{
	if(k==n+1)
	{	
		for(int i=head1[ans%MAXN1];i;i=tr1[i].nxt)
		{
			int to=tr1[i].to;
			if(to==ans)
			{
				pd=1;break;
			}
		}
		if(pd==0) return;
	}
	if(pd==1) return;
	if(ans-a[k]>=0)
	{
		vis[k]=1;
		ans-=a[k];

		dfs1(k+1);
		ans+=a[k];
	}
	vis[k]=0;
	dfs1(k+1);
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		memset(head1,0,sizeof(head1));
		cnt=0;
		pd=0;ans=m;sum[0]=0;
		for(int i=1;i<=n;i++) 
		{
			scanf("%d",&a[i]);
			sum[i]=sum[i-1]+a[i];
			if(a[i]==m||sum[i]==m) pd=1;
		}
		if(sum[n]<m)
		{
			printf("NO\n");
			continue;
		}
		sort(a+1,a+n+1,comp1);
		dfs(1);
		dfs1(n/2+1);
		if(pd==1) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值