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\)都代表纸带上格子的编号
需要满足一下条件:
-
\(x,y,z\)都是整数,\(x<y<z\),\(y-x=z-y\)
-
\(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)