【qboi冲刺NOIP2017复赛试题4】 全套题目+题解+程序

时间:2020-12-29 19:43:11

作为一个好人(验题人),我给大家奉上下这套题的题解,并且预祝大家这套题能够AK:

T1题面:Alice现在有n根木棍,他们长度为1,2,3....n,Bob想把某一些木棍去掉,使得Alice剩下的木棍任意3根不能构成三角形。Bob想知道至少他需要去掉多少根。

题解:不难发现,这一题所求为在[1,n]中有多少个数不是斐波那契数,因为n的范围很小,直接枚举即可。

 #include<iostream>
#include<cstdio>
#include<cstring>
#define L long long
using namespace std;
L n,a=,b=,c; int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
cin>>n;
if(n<=) {printf("0\n"); return ;}
int i;
for(i=;a+b<=n;i++){
c=a+b;
a=b; b=c;
}
cout<<n-i<<endl;
}

第二题题面:给你n堆石子,每堆石子有ai个石子,对于每一次操作,可以将某堆的一个石子移动到另外一堆。游戏终止的条件是:存在一个x(x>1),使得任意一堆石子满足:ai%x==0。(1<=i<=n)请求出游戏终止的最小操作数。

我们不难发现,x必为sum的因子,枚举所有的因子d,对于该因子d,将a数组中每一个数取模并排序,最后贪心地扫一遍即可。

时间复杂度为$O(n*d(sum^{0.5}))$

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 110000
#define L long long
using namespace std;
int a[M]={},b[M]={},dd[M]={},n,dn;
L ans=,minn=1e18; void sort(int x){
for(int i=;i<=n;i++) dd[b[i]]++;
int cnt=;
for(int i=;i<x;i++){
while(dd[i]) b[++cnt]=i,dd[i]--;
}
} int main(){
cin>>n;
for(int i=;i<=n;i++) scanf("%d",a+i),ans+=a[i];
for(int d=;d<=;d++) if(ans%d==){
L sum=,cnt=;
for(int i=;i<=n;i++) b[i]=a[i]%d;
sort(d);
//sort(b+1,b+n+1);
for(int i=,j=n;i<j;i++){
while(b[i]){
int delta=min(b[i],d-b[j]);
cnt+=delta;
b[j]+=delta;
b[i]-=delta;
if(b[j]==d) j--;//!!!
}
}
minn=min(minn,cnt);
}
cout<<minn<<endl;
}
 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 110000
#define L long long
using namespace std;
int a[M]={},b[M]={},dd[M]={},n,dn;
L ans=,minn=1e18; void sort(int x){
for(int i=;i<=n;i++) dd[b[i]]++;
int cnt=;
for(int i=;i<x;i++){
while(dd[i]) b[++cnt]=i,dd[i]--;
}
} int main(){
cin>>n;
for(int i=;i<=n;i++) scanf("%d",a+i),ans+=a[i];
for(int d=;d<=;d++) if(ans%d==){
L sum=,cnt=;
for(int i=;i<=n;i++) b[i]=a[i]%d;
sort(d);
//sort(b+1,b+n+1);
for(int i=,j=n;i<j;i++){
while(b[i]){
int delta=min(b[i],d-b[j]);
cnt+=delta;
b[j]+=delta;
b[i]-=delta;
if(b[j]==d) j--;//!!!
}
}
minn=min(minn,cnt);
}
cout<<minn<<endl;
}

第三题题解:简单乱搞题,直接暴力枚举即可。

 #include<iostream>
#include<cstdio>
#include<cstring>
#define M 1100
using namespace std;
int n,m,d;
struct pt{
int x,y; pt(){x=y=;}
pt(int xx,int yy) {x=xx; y=yy;}
}a[M];
int f[M]={},ok[M]={};
int get(int x){
if(f[x]!=x) return f[x]=get(f[x]);
return x;
}
int pf(int x){return x*x;}
bool cmp(int x,int y){
return pf(a[x].x-a[y].x)+pf(a[x].y-a[y].y)<=d*d;
}
struct edge{int u,next;}e[M*M]={}; int head[M]={},use=;
void add(int x,int y){use++;e[use].u=y;e[use].next=head[x]; head[x]=use;}
int main(){
scanf("%d%d%d",&n,&m,&d);
for(int i=;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
for(int i=;i<=n;i++){
for(int j=;j<=n;j++) if(i!=j)
if(cmp(i,j))
add(i,j);
}
for(int i=;i<=n;i++) f[i]=i;
while(m--){
char c[]; int x,y;
scanf("%s%d",&c,&x);
if(c[]=='O'){
ok[x]=;
for(int i=head[x];i;i=e[i].next) if(ok[e[i].u]){
int xx=get(x),yy=get(e[i].u);
if(xx==yy) continue;
f[xx]=yy;
}
} else{
scanf("%d",&y);
x=get(x); y=get(y);
if(x==y) printf("YES\n");
else printf("NO\n");
}
}
}

第四题题解:我们对所所有的买卖方案按 Q-P 的大小 ,从小到大排序,然后直接跑01背包即可。

 #include <bits/stdc++.h>
using namespace std;
struct node
{
int p,q,v;
node(){}
bool operator < (const node &a) const
{
return q-p < a.q - a.p;
}
void read() {scanf("%d%d%d",&p,&q,&v);}
}a[];
int f[];
int main()
{
int n,m;
while (scanf("%d%d",&n,&m)!=EOF)
{
memset(f,,sizeof(f));
for (int i=; i<=n; i++) a[i].read();
sort(a+,a++n);
for (int i=; i<=n; i++)
for (int j=m; j>=a[i].q; j--)
f[j] = max(f[j], f[j-a[i].p]+a[i].v);
printf("%d\n",f[m]);
}
}

是不是很简单啊??