【NOIP-普及组-复赛】(7)NOIP2016普及组复赛题解

NOIP2016终于搞完了。。。
都是好题。。。
————————————华丽的分割线————————————
第一题:
第一题题目-第1页
第一题题目-第2页
第一题题目-第3页
首先,这是一道O1好题
把n除以每个个数再乘价格,取最大值即可
不过要判断能否整除,我有一个好方法大家可以看看(用了这个方法就是一道顺序结构题了)
不AC打peegoo
代码:

#include <cstdio>
#include <algorithm>

int a[5],b[5],ans[5],n;
int main()
{
	freopen("pencil.in","r",stdin);
	freopen("pencil.out","w",stdout);
	register int i,minn=2147483647;
	
	scanf("%d",&n);
	for(i=1;i<=3;i++)scanf("%d%d",&a[i],&b[i]);
	for(i=1;i<=3;i++)minn=std::min(minn,(n+a[i]-1)/a[i]*b[i]);
	return !printf("%d\n",minn);
}

好短
————————————华丽的分割线————————————
第二题:
第二题题目-第1页
第二题题目-第2页
第二题题目-第3页
这是一道乱搞好题
这种字符串题目很可怕。。。一般都要乱搞
所以我就把主算法说一遍,其他的自行处理(我写了70多行,还用了从没写过的struct内定义函数。。。)
首先我们从起始年搜到终点年(起,终点要特判)
然后对于每一年都自动生成他的回文日月(比如说9220年->2月29日)
然后switch判断是这个月30还是31天还是2月还是没有
然后判断日期是否超过
现在专门讲判断闰年
最土的方法就是按条件一个if啦(我就是这样)
然后你会发现如果是整百年月数为0,所以只用mod4
然后你又发现只有年=9220才用判,所以就可以乱搞
现在讲另一种
这就是传说中的—搜索日月判年份
首先你要有一个比较两个日期串大小的函数(用strcmp 石头人触摸屏 (奇怪输入法)就行了)
然后你要把一年366(别问我为什么)天遍历一遍
然后看是否在起点到终点之间
至于闰年嘛。。。9220肯定成立,就不用管了
代码:

#include <cstdio>

struct date
{
	char date[10];
	int year()
	{
		return (date[0]-'0')*1000+(date[1]-'0')*100+(date[2]-'0')*10+date[3]-'0';
	}
	int month()
	{
		return (date[3]-'0')*10+date[2]-'0';
	}
	int day()
	{
		return (date[1]-'0')*10+date[0]-'0';
	}
	int month2()
	{
		return (date[4]-'0')*10+date[5]-'0';
	}
	int day2()
	{
		return (date[6]-'0')*10+date[7]-'0';
	}
}begin,end;
int ans;
date change1(int y)
{
	date ans;
	register int i;
	
	
	for(i=3;i>=0;i--)ans.date[i]=y%10+'0',y/=10;
	return ans;
}
bool work(int y)
{
	date now=change1(y);
	register int m=now.month(),d=now.day();
	
	if(y==begin.year()&&begin.month2()>m)return 0;
	if(y==begin.year()&&begin.month2()==m&&begin.day2()>d)
	return 0;
	if(y==end.year()&&end.month2()<m)return 0;
	if(y==end.year()&&end.month2()==m&&end.day2()<d)return 0;
	switch(m)
	{
		case 1:case 3:case 5:case 7:case 8:case 10:case 12:
		return d<=31;
		case 4:case 6:case 9:case 11:return d<=30;
		case 2:if(y%400||(!(y%4)&&(y%100)))return d<=29;
		else return d<=28;
		default:return 0;
	}
}
int main()
{
	freopen("date.in","r",stdin);
	freopen("date.out","w",stdout);
	register int i;
	
	scanf("%s%s",begin.date,end.date);
	for(i=begin.year();i<=end.year();i++)ans+=work(i);
	return !printf("%d\n",ans);
}

————————————华丽的分割线————————————
第三题:
第三题题目-第1页
第三题题目-第2页
第三题题目-第3页
这是一道送分好题。。。
然而我爆0了
代码一部分:

const int MAXN=10086;
int a[MAXN],b[MAXN],c[MAXN][MAXN*3],now[MAXN],ans,n,away=1;

这题是有史以来最水的第三题(但是我炸了)
我们可以每次来船时加入所有乘客和他们的信息(在哪个船不用记),并输出国家数。
由于国家数比较少,可以用一个数组记有没有某个国的人。
然后就完了
代码片:

#include <cstdio>

const int MAXN=100086;
int c[MAXN*3][2],now[MAXN],ans,n,away=1,in=0;
int main()
{
//    freopen("port.in","r",stdin);
//    freopen("port.out","w",stdout);
    register int i,j,a,b;
    
    scanf("%d",&n);
    
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&a,&b);
        for(j=1;j<=b;j++)
        {
            scanf("%d",&c[++in][0]);
            ans+=!(now[c[in][0]]++);
            c[in][1]=a;
        }
        while(away<=in&&c[away][1]<=a-86400)
            ans-=!(--now[c[away++][0]]);
        printf("%d\n",ans);
    }
    return 0;
}

————————————华丽的分割线————————————
第四题:
第四题题目-第1页
第四题题目-第2页
第四题题目-第3页
一道骗分好题。。。
首先这是数论大家都能看出来吧
然后我们先说骗分的算法(毕竟是最后一题)
首先是m^4的:
把abcd枚举(注意重复),然后判断。
然后是n^4的:
这里要用到一个技巧,就是以魔法值为下标存(n),对于每个魔法阵,都用魔法值为a,b,c,d的物品个数相乘来当做新增魔法阵的个数(这也是正解的一个技巧),对于各个魔法值为a,b,c,d的物品,将新增魔法阵的个数除a(或b,c,d)的个数就行了。
然后是m^3的
这个也还好,就是通过abc来推d。
然后是n^3的
我就是用这种方法,只是加了上面的技巧
好了我们讲正解(n^2)
首先我们应该注意到两个等式都用到了a*b,另一个告诉了我们abcd的关系
所以我们化简一下,得b-a=2(d-c),3(b-a)=c-b
Q:然而你并没有化简多少啊
A:你要是在化简下去你就输了(和我一样)
然后我们用一个神奇的方法:由于abcd大小给定,我们用数轴简化一下
ab=2cd,3ab=bc(考场上就是没想到这个啊啊)
然后把cd设为k,得ab=2k,bc=6k,cd=k。

所以cd小于n/9
我们枚举cd长度,显然d的位置越高,匹配次数越多(单调不减),每个D比前一个D多出来的匹配次数就是多出来的满足bc>ab*3的b的数量,所以我们用一次循环统计出所有长度固定的D位置和C位置匹配次数,再把ab给搞一遍,然后统计答案,就做完了。
时间复杂度:n^2
Q:你不担心炸吗最后一个数据n=15000啊
A:我们有常数(/9)
代码:

#include <cstdio>
#include <cstring>

const int MAXN=15051,MAXM=10086*4;
int ans[MAXN][5],n,m,map[MAXN],a[MAXM],x[MAXN],y[MAXM];
int main()
{
	register int i,j,k;
	
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++)scanf("%d",&a[i]),map[a[i]]++;
	for(i=1;i*9<n;i++)
	{
		memset(x,0,sizeof(x)),memset(y,0,sizeof(y));
		for(j=i*2,k=0;j+7*i<n;j++,k++)x[j]=x[j-1]+map[j]*map[k];
		for(j=n-i,k=n;j>8*i;j--,k--)y[j]=y[j+1]+map[j]*map[k];
		for(j=i*2,k=0;j+7*i<n;j++,k++)
		{
			ans[j][2]+=y[j+6*i+1]*map[k];
			ans[k][1]+=y[j+6*i+1]*map[j];
		}
		for(j=n-i,k=n;j>8*i;j--,k--)
		{
			ans[j][3]+=x[j-6*i-1]*map[k];
			ans[k][4]+=x[j-6*i-1]*map[j];
		}
	}
	for(i=1;i<=m;i++,printf("\n"))for(j=1;j<=4;j++)
		printf("%d ",ans[a[i]][j]);
	return 0;
}

————————————华丽的分割线————————————
今年题目都很长啊
基本法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值