又是周复一周的考试时间啦~
然而因为粗心大意而丢分真是。。让人很不爽。。。
0+0+15;
第一题没有特判a=0的情况并且数组开小了,直接炸掉;
第二题。。。早知道就不写正解了,好歹可以混上十分;
第三题,唯一一道乱搞题,连题目都没有读完,竟然水过了三个点。。
例行感谢WKL凯爷的良苦用心~
上题。
1.Fibonacci
题目描述
豆豆最近迷上了 Fibonacci 数,然后他开始研究 Fibonacci 数的乘积。现在他想问你某个数能不能分解成两个 Fibonacci 数的乘积?
Fibonacci 数的定义:F0=0,F1=1,Fk=Fk-1+Fk-2 。
输入格式
第一行一个整数 T 代表提问次数。
接下来 T 行,每行一个数字 A 表示豆豆询问你的数。
输出格式
对于每次提问,如果这个数可以被分解成两个 Fibonacci 数的成绩输出“Yes”,否则输出“No”。
样例数据
输入
5
5
4
12
11
10
输出
Yes
Yes
No
No
Yes
备注
【数据范围】
对于 50% 的数据:A≤50;
对于 100% 的数据:T≤100;0≤A≤10^9 。
首先预处理一下f数组,找出10^9以内的所有斐波那契数。
然后枚举就可以了,大概数组开到50就可以保证通过,同时一定一定要记住判a=0的情况。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
int t,a,tot,ff;
int f[55],b[55];
inline int read()
{
int i=0;char c;
for(c=getchar();c<'0'||c>'9';c=getchar());
for(;c>='0'&&c<='9';c=getchar())
i=(i<<1)+(i<<3)+c-'0';
return i;
}
int main()
{
//freopen("fib.in","r",stdin);
//freopen("fib.out","w",stdout);
memset(f,0,sizeof(f));
t=read();
f[0]=0,f[1]=1;
for(int i=2;i<=50;i++)
f[i]=f[i-1]+f[i-2];
while(t--)
{
ff=0;tot=0;
a=read();
if(a==0)
{
cout<<"Yes"<<endl;
continue;
}
for(int i=1;i<=50;i++)
if(a%f[i]==0)
b[++tot]=f[i];
else if(a<f[i]) //其实直接枚举就可以了,这样降低了一部分时间复杂度。
break;
for(int i=1;i<=tot;i++)
{
if(ff==1)
break;
int aa=a/b[i];
for(int j=1;j<=tot;j++)
if(aa==b[j])
{
ff=1;
break;
}
else if(aa<b[j])
break;
}
if(ff==1)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}
2.一样远
题目描述
企鹅国的城市结构是一棵树,有 N 座城市和 N-1 条无向道路,每条道路都一样长。豆豆和豆沙准备去参加 NOIP(National Olympiad in Informatics for Penguin),但是他们住在不同的地方,豆豆住在城市 A ,豆沙住在城市 B 。他们想找一个距离 A 和 B 一样远的集合地点,所以他们想知道有多少个城市满足这个要求?
由于他们会参加很多次 NOIP ,所以有很多个询问。
输入格式
第一行一个整数 N,代表城市个数。
接下来 N-1 行,每行两个数字 F 和 T ,表示城市 F 和城市 T 之间有一条道路。
接下来一行一个整数 M 代表询问次数。
接下来 M 行,每行两个数字 A 和 B ,表示这次询问的城市 A 和城市 B(A可能与B相同)。
输出格式
输出 M 行,每行一个整数表示到 A 和 B 一样远的城市个数。
样例数据 1
输入
4
1 2
2 3
2 4
2
1 2
1 3
输出
0
2
样例数据 2
输入
4
1 2
2 3
2 4
2
1 1
3 3
输出
4
4
备注
【数据范围】
对于 30% 的数据:N,M≤1000;
对于另外 10% 的数据:A=B;
对于另外 30% 的数据:保证树的形态随机;
对于 100% 的数据:1≤N,M≤100000。
这道题用LCA来做。
如果A和B的深度相同,就找到二者的最近公共祖先,LCA以及之上的所有点都符合题意;
如果A和B的深度不同,找到二者的中点,中点以及其子树都符合题意。
至于求中点,先用LCA求出A和B之间的距离,然后倍增求出中点。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
struct node
{
int to,next;
}bian[200010];
int n,x,y,z,tot,m,ans,len,e,f;
int first[200010],size[100010],dep[100010],fa[100010][25];
inline int read()
{
int i=0;char c;
for(c=getchar();c<'0'||c>'9';c=getchar());
for(;c>='0'&&c<='9';c=getchar())
i=(i<<1)+(i<<3)+c-'0';
return i;
}
inline void add(int x,int y)
{
tot++;
bian[tot].to=y;
bian[tot].next=first[x];
first[x]=tot;
}
inline void dfs(int u,int f)
{
fa[u][0]=f;
for(int i=1;i<=20;i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(int e=first[u];e;e=bian[e].next)
{
int v=bian[e].to;
if(v!=f)
{
dep[v]=dep[u]+1;
dfs(v,u);
size[u]+=size[v];
}
}
}
inline int lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
int d=dep[x]-dep[y];
for(int i=20;i>=0;i--)
if(d&(1<<i))
x=fa[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main()
{
//freopen("equal.in","r",stdin);
//freopen("equal.out","w",stdout);
n=read();
for(int i=1;i<n;i++)
{
x=read(),y=read();
add(x,y);
add(y,x);
}
for(int i=1;i<=n;i++)
size[i]=1;
dep[1]=0;
dfs(1,0);
m=read();
while(m--)
{
x=read(),y=read();
if(x==y)
{
printf("%d\n",n);
continue;
}
z=lca(x,y);
len=dep[x]+dep[y]-2*dep[z]; //求距离。
if(len%2==1)
{
printf("0\n");
continue;
}
if(dep[x]<dep[y])
swap(x,y);
int d=len/2;
if(dep[x]==dep[y]) //若深度相同。
{
e=x,f=y;
for(int i=20;i>=0;i--)
if((d-1)&(1<<i))
e=fa[e][i];
for(int i=20;i>=0;i--)
if((d-1)&(1<<i)) //这两段代码和LCA一样。
f=fa[f][i];
ans=n-size[e]-size[f];
}
else //若不同。
{
e=x,f=x;
for(int i=20;i>=0;i--)
if(d&(1<<i))
e=fa[e][i];
for(int i=20;i>=0;i--)
if((d-1)&(1<<i))
f=fa[f][i];
ans=size[e]-size[f];
}
cout<<ans<<endl;
}
return 0;
}
3.拆网线
题目描述
企鹅国的网吧们之间由网线互相连接,形成一棵树的结构。现在由于冬天到了,供暖部门缺少燃料,于是他们决定去拆一些网线来做燃料。但是现在有 K 只企鹅要上网和别人联机游戏,所以他们需要把这 K 只企鹅安排到不同的机房(两只企鹅在同一个机房会吵架),然后拆掉一些网线,但是需要保证每只企鹅至少还能通过留下来的网线和至少另一只企鹅联机游戏。
所以他们想知道,最少需要保留多少根网线?
输入格式
第一行一个整数 T ,表示数据组数;
每组数据第一行两个整数 N,K ,表示总共的机房数目和企鹅数目。
第二行 N-1 个整数,第 i 个整数 Ai 表示机房 i+1 和机房 Ai 有一根网线连接(1≤Ai≤i)。
输出格式
每组数据输出一个整数表示最少保留的网线数目。
样例数据
输入
2
4 4
1 2 3
4 3
1 1 1
输出
2
2
备注
【数据范围】
对于 30% 的数据:N≤15;
对于 50% 的数据:N≤300;
对于 70% 的数据:N≤2000;
对于 100% 的数据:2≤K≤N≤100000,T≤10。
这道题其实比第二题要简单一些。
分析题意可以得知,要想最后剩下的网线最少,那么企鹅应尽可能的两两配对,这样两只企鹅只用一根网线。
首先将边存入,然后计算出两两配对的点数。若点数加到k还未加完,则退出循环直接输出 此时的点数/2。若未加到k就加完了所有两两配对的点,则余下的k-cnt个点要想加进去必须一个点带一条边,则ans+=k-cnt。
计算点数的时候用深度优先搜索(DFS),从最下面开始枚举,若当前边的两个端点都没有被配对过,则cnt+=2,ans++,表示增加了一对两两配对的点。然后对这两个点进行标记表示已经配对。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
int t,n,k,x,cnt,ans,tot;
int next[200010],to[200010],first[100010];
bool flag[100010];
inline int read()
{
int i=0;char c;
for(c=getchar();c<'0'||c>'9';c=getchar());
for(;c>='0'&&c<='9';c=getchar())
i=(i<<1)+(i<<3)+c-'0';
return i;
}
inline void add(int x,int y)
{
tot++;
next[tot]=first[x];
first[x]=tot;
to[tot]=y;
}
inline void zql(int u,int fa)
{
for(int i=first[u];i;i=next[i])
{
int v=to[i];
if(v!=fa)
{
zql(v,u);
if(!flag[v]&&!flag[u])
{
if(cnt<k)
ans++;
cnt+=2;
flag[v]=true;
flag[u]=true;
}
}
}
}
int main()
{
//freopen("tree.in","r",stdin);
//freopen("tree.out","w",stdout);
t=read();
while(t--)
{
memset(first,0,sizeof(first));
memset(flag,0,sizeof(flag));
tot=0,cnt=0,ans=0;
n=read(),k=read();
for(int i=1;i<n;i++)
{
x=read();
add(x,i+1);
add(i+1,x);
}
zql(1,0);
if(cnt<k)
ans+=k-cnt; //除去两两配对的点对,剩下的每加一个点都要加一条边。
cout<<ans<<endl;
}
return 0;
}
明天又要考试。。。心累。。。。伐开心
来自2017.10.23.
——我认为return 0,是一个时代的终结。