Loading

NOIP2015&2016普及组解题报告

NOIP2015普及组题目下载

NOIP2016普及组题目下载

NOIP2015普及组题目:

NOIP2016普及组题目 # T1 金币$(coin.cpp/c/pas)$ ## 题意 第一天,骑士收到一个金币,第二、三天,收到两个金币,第四、五、六天,收到三个金币......问$k$天,骑士一共收到几个金币? ## 数据范围 对于 100%的数据,$1 ≤ K ≤ 10,000$。 ## 思路 ~~这道题很水~~

看完数据范围,果断枚举啦。

废话不多说,我知道你们想看这个:(头文件就不传了,如果八道题都传的话,我的博客会变得很长。。。)

#define ll long long
using namespace std;
inline ll read(){
	char ch=getchar();ll res=0,f=1;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
	return res*f;
}
inline void write(ll zx){
	if(zx<0) zx=-zx,putchar('-');
	if(zx<10) putchar(zx+'0');
	else{
		write(zx/10);
		putchar(zx%10+'0');
	}
}
ll n,ans,k=0;
int main(){
	freopen("coin.in","r",stdin);freopen("coin.out","w",stdout);
	n=read(); //输入
	for(ll i=1;k<n;i++){ //表示该天要加多少金币
		for(ll j=1;j<=i;j++){ //持续i天
			ans+=i; //答案记录
			++k; //记住是k天哦
			if(k==n) break; //有可能k天并不能刚好取完,所以加一个break
		}
	}
	write(ans);putchar('\n'); //输出
	return 0;
}

T2 扫雷游戏\((mine.cpp/c/pas)\)

题意

玩一下windows系统下的扫雷你就知道了。(手动滑稽)

每一个格子要不是雷,那就是数字。

雷:表示这个格子里有地雷。

数字:表示这个格子四周8个格子的雷的总数。

问:在某一个状态下,扫雷系统的状态。

数据范围

对于 100%的数据,\(1≤n≤100,1≤m≤100\)

思路

这道题暴力就好了啊233333

这道题有两个思路:

1.枚举一下每一个格子,如果是数字,扫一遍其周围的格子,如果是雷,这个数字+1,如果是雷,那么就直接输出*。

2.枚举一下每一个格子,如果是雷,将四周的格子数字+1。最后输出的时候如果是雷,那么直接输出,否则输出之前累加的数字。

inline int read(){
	char ch=getchar();int res=0,f=1;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
	return res*f;
}
inline void write(int zx){
	if(zx<0) zx=-zx,putchar('-');
	if(zx<10) putchar(zx+'0');
	else{
		write(zx/10);
		putchar(zx%10+'0');
	}
}
char a[110][110];
int f[110][110];
int n,m;
void work(int x,int y){
	f[x-1][y]++;
	f[x-1][y-1]++;
	f[x-1][y+1]++;
	f[x][y-1]++;
	f[x][y+1]++;
	f[x+1][y-1]++;
	f[x+1][y]++;
	f[x+1][y+1]++;
}
int main(){
	freopen("mine.in","r",stdin);freopen("mine.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			char ch;
			cin>>ch;
			a[i][j]=ch;
			if(ch=='*') work(i,j); //采用方法2,如果是雷,拓展四周
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]=='*') putchar('*'); //如果是雷
			else write(f[i][j]); //如果是数字
		}
		putchar('\n');
	}
	return 0;
}

T3 求和 \((sum.cpp/c/pas)\)

题意

一条狭长的纸带被均匀划分出了\(n\)个格子,格子编号从\(1\)\(n\)。每个格子上都染了一种颜色\(colori\)(用\([1,m]\)当中的一个整数表示),并且写了一个数字\(numberi\)

定义一种特殊的三元组:\((x, y, z)\),其中\(x,y,z\)都代表纸带上格子的编号

需要满足一下条件:

  1. \(x,y,z\)都是整数,\(x<y<z\),\(y-x=z-y\)

  2. \(colorx=colorz\)

问:所有满足以上条件的三元组的\((x + z) ∗ (𝑛𝑢𝑚𝑏𝑒𝑟𝑥 + 𝑛𝑢𝑚𝑏𝑒𝑟𝑧)\)之和为多少?

数据范围

对于第\(1\)组至第\(2\)组数据,\(1 ≤ n ≤ 100, 1 ≤ m ≤ 5\)

对于第\(3\)组至第\(4\)组数据,\(1 ≤ n ≤ 3000, 1 ≤ m ≤ 100\)

对于第\(5\)组至第\(6\)组数据,\(1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000\),且不存在出现次数超过\(20\)的颜色;

对于全部\(10\)组数据,\(1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000, 1 ≤ colori ≤ m, 1 ≤ numberi ≤ 100000\)

思路

对于20%的数据

直接枚举三元组(x、y、z)。
代码就不贴了。

对于40%的数据

先将数组按顺序排序,枚举x、z判断一下y是否有解。

对于60%的数据

先将数组按顺序排序,将所有数字按颜色分组,再对于每个颜色进行暴力枚举,其他和40分方法一样。

对于100%的数据

这个时候就要使用到数学方法了。

先把数组中每个数字按照颜色分组。

根据\(y-x=z-y\)可以得出:\(2*y=z+x\)

那么等号的左边是一个偶数,等号右边也应该是一个偶数。
那么z、x应该同奇偶。

那么一开始按颜色分组时,颜色相同,再按奇偶分组。

那么可以得出:

ans+=i*((num[a[i].color][i%2]-2)*a[i].number+sum[a[i].color][i%2])

其中num表示该颜色、奇偶所出现的次数,sum表示其数字之和。

#define MOD 10007 
#define ll long long
using namespace std;
inline ll read(){
	char ch=getchar();ll res=0,f=1;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
	return res*f;
}
inline void write(ll zx){
	if(zx<0) zx=-zx,putchar('-');
	if(zx<10) putchar(zx+'0');
	else{
		write(zx/10);
		putchar(zx%10+'0');
	}
}
ll n,m,ans;
struct node{
	ll number,color;
}a[100010];
ll num[100010][5],sum[100010][5];
int main(){
	freopen("sum.in","r",stdin);freopen("sum.out","w",stdout);
	n=read();m=read();
	for(ll i=1;i<=n;i++) a[i].number=read();
	for(ll i=1;i<=n;i++) a[i].color=read();
	for(ll i=1;i<=n;i++){
		++num[a[i].color][i%2];
		sum[a[i].color][i%2]+=a[i].number;
		sum[a[i].color][i%2]%=MOD;
	}
	for(ll i=1;i<=n;i++){
		ans+=i*((num[a[i].color][i%2]-2)*a[i].number+sum[a[i].color][i%2]);
		ans%=MOD;
	}
	write(ans);putchar('\n');
	return 0;
}

T4 推销员\((salesman.cpp/c/pas)\)

题意

螺丝街一共有\(N\)家住户,第\(i\)家住户到入口的距离为\(Si\)米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依次向螺丝街的\(X\)家住户推销产品,然后再原路走出去。阿明每走\(1\)米就会积累\(1\)点疲劳值,向第\(i\)家住户推销产品会积累\(Ai\)点疲劳值。阿明是工作狂,他想知道,对于不同的\(X\),在不走多余的路的前提下,他最多可以积累多少点疲劳值。

数据范围

对于 20%的数据,\(1≤N≤20\)

对于 40%的数据,\(1≤N≤100\)

对于 60%的数据,\(1≤N≤1000\)

对于 100%的数据,\(1≤N≤100000\)

思路

部分分

部分分其实就是枚举啦,可以用单调队列优化.....

100分做法

贪心其实就够了。

考虑我们如何将答案最大化:对于每个x,一定是选择(一个最大的s)+(x-1个最大的a)或者x个最大的a,可以使答案最优那么具体怎么实现呢
我们先把数组按照a排序
我们用sum[i]表示a数组的前i项的和,q[i]表示s数组的前i项的最大值,h[i]表示a[i]2+s[i]后i项的最大值,对于每个x,他的答案就是max(sum[x]+2q[x],sum[x-1]+h[x])

转自luogu的一位大佬。

#define ll long long
using namespace std;
inline ll read(){
	char ch=getchar();ll res=0,f=1;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
	return res*f;
}
inline void write(ll zx){
	if(zx<0) zx=-zx,putchar('-');
	if(zx<10) putchar(zx+'0');
	else{
		write(zx/10);
		putchar(zx%10+'0');
	}
}
ll n,h[100010],q1[100010],q2[100010];
struct node{
	ll s,v;
}a[100010];
bool cmp(node qx,node qy){return qx.v>qy.v;}
int main(){
	freopen("salesman.in","r",stdin);freopen("salesman.out","w",stdout);
	n=read();
	for(ll i=1;i<=n;i++) a[i].s=read();
	for(ll i=1;i<=n;i++) a[i].v=read();
	sort(a+1,a+n+1,cmp);
	for(ll i=n;i>=1;i--) h[i]=max(h[i+1],2*a[i].s+a[i].v);
    for(ll i=1;i<=n;i++) q1[i]=max(q1[i-1],a[i].s);
    for(ll i=1;i<=n;i++) q2[i]=q2[i-1]+a[i].v;
    for(ll i=1;i<=n;i++) printf("%lld\n",max(q2[i-1]+h[i],q2[i]+2*q1[i]));
	return 0;
}

(占一个坑,以后再填NOIP2016)

posted @ 2018-11-08 19:21  yzx_1798106406  阅读(255)  评论(0编辑  收藏  举报