Problem A: Fibonacci(fib.pas/cpp)
Time Limit: 1000 ms Memory Limit: 128 MB
Description
豆豆最近迷上了Fibonacci数,然后他开始研究Fibonacci数的乘积。现在他想问你某个数能不能分解成两个Fibonacci数的乘积?
Fibonacci数的定义:F0=0,F1=1,Fk=Fk-1+Fk-2。
Input
第一行一个整数T代表提问次数。
接下来T行,每行一个数字A表示豆豆询问你的数。
Output
对于每次提问,如果这个数可以被分解成两个Fibonacci数的乘积输出“Yes”,否则输出“No”。
Sample Input
5
5
4
12
11
10
Sample Output
Yes
Yes
No
No
Yes
HINT
对于50%的数据:A≤50;
对于100%的数据:T≤100,0≤A≤10^9 。
Solution
结论:在int范围内,斐波那契数列只有46项
所以每次直接判就可以了。
#include<bits/stdc++.h>
using namespace std;
#define int long long
map<int,int>mp;
int f[201];
void pre(){
f[1]=1;
for(int i=2;i<=46;++i){
f[i]=f[i-1]+f[i-2];
if(i>2)mp[f[i]]=1;
}
}
signed main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
pre();
int T;
scanf("%lld",&T);
while(T--){
int tmp;
scanf("%lld",&tmp);
if(tmp==0||tmp==1){puts("Yes");continue;}
bool flag=false;
for(int i=1;i<=46;++i){
if(tmp%f[i]==0){
if(mp[tmp/f[i]]){
flag=true;
break;
}
}
}
puts(flag?"Yes":"No");
}
}
Problem B: 一样远(equal.pas/cpp)
Time Limit: 1000 ms Memory Limit: 128 MB
Description
企鹅国的城市结构是一棵树,有N座城市和N-1条无向道路,每条道路都一样长。豆豆和豆沙准备去参加NOIP(National Olympiad in Informatics for Penguin),但是他们住在不同的地方,豆豆住在城市A,豆沙住在城市B。他们想找一个距离A和B一样远的集合地点,所以他们想知道有多少个城市满足这个要求?
由于他们会参加很多次NOIP,所以有很多个询问。
Input
第一行一个整数N代表城市个数。
接下来N-1行,每行两个数字F和T,表示城市F和城市T之间有一条道路。
接下来一行一个整数M代表询问次数。
接下来M行,每行两个数字A和B,表示这次询问的城市A和城市B(A可能与B相同)。
Output
输出M行,每行一个整数表示到A和B一样远的城市个数。
Sample Input
4
1 2
2 3
2 4
2
1 2
1 3
Sample Output
0
2
HINT
对于30%的数据:N,M≤1000;
对于另外10%的数据:A=B;
对于另外30%的数据:保证树的形态随机;
对于100%的数据:1≤N,M≤100000。
Solution
设查询的两个点为x、y,他们的最近公共祖先为lca
那么分4种情况讨论:
1、x=y:这种情况下整棵树都可以。
2、dis(x,y)为奇数:显然没有一个点满足。
3、dis(x,y)为偶数但dis(x,lca)!=dis(y,lca):此时找到中间点,那么中间点的子树大小就是答案。
4、dis(x,lca)=dis(y,lca):除了lca的包含x和包含y的子树以外,全部符合条件。
然后随便rush就可以了。
#include<bits/stdc++.h>
using namespace std;
struct qwq{
int v;
int nxt;
}edge[400001];
int cnt=-1;
int head[400001];
void add(int u,int v){
edge[++cnt].nxt=head[u];
edge[cnt].v=v;
head[u]=cnt;
}
int f[400001][21];
int dep[400001];
int siz[400001];
void dfs(int u){
siz[u]=1;
for(int i=1;i<=20;++i){
f[u][i]=f[f[u][i-1]][i-1];
}
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].v;
if(v==f[u][0])continue;
dep[v]=dep[u]+1;
f[v][0]=u;
dfs(v);
siz[u]+=siz[v];
}
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
int d=dep[x]-dep[y];
for(int i=0;i<=20;++i){
if(d&(1<<i)){
x=f[x][i];
}
}
if(x==y){
return x;
}
for(int i=20;i>=0;--i){
if(f[x][i]==f[y][i])continue;
x=f[x][i],y=f[y][i];
}
return f[x][0];
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("ans.txt","w",stdout);
memset(head,-1,sizeof(head));
int n;
scanf("%d",&n);
for(int i=2;i<=n;++i){
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs(1);
int m;
scanf("%d",&m);
for(int i=1;i<=m;++i){
int x,y;
scanf("%d%d",&x,&y);
if(x==y){
printf("%d\n",n);
continue;
}
int lca=LCA(x,y);
if(dep[x]-dep[lca]==dep[y]-dep[lca]){
int mid=dep[x]-dep[lca]-1;
for(int j=0;j<=20;++j){
if(mid&(1<<j)){
x=f[x][j];y=f[y][j];
}
}
printf("%d\n",n-siz[x]-siz[y]);
continue;
}
if((dep[x]+dep[y])%2==1){
puts("0");
continue;
}
int mid=(dep[x]-dep[lca]+dep[y]-dep[lca])/2;
int mid1=mid-1;
if(dep[x]<dep[y])swap(x,y);
int xx=x;
for(int j=0;j<=20;++j){
if(mid&(1<<j)){
x=f[x][j];
}
if(mid1&(1<<j)){
xx=f[xx][j];
}
}
//cout<<x<<endl;
printf("%d\n",siz[x]-siz[xx]);
}
}
Problem C: 拆网线(tree.pas/cpp)
Time Limit: 1000 ms Memory Limit: 128 MB
Description
企鹅国的网吧们之间由网线互相连接,形成一棵树的结构。现在由于冬天到了,供暖部门缺少燃料,于是他们决定去拆一些网线来做燃料。但是现在有K只企鹅要上网和别人联机游戏,所以他们需要把这K只企鹅安排到不同的机房(两只企鹅在同一个机房会吵架),然后拆掉一些网线,但是需要保证每只企鹅至少还能通过留下来的网线和至少另一只企鹅联机游戏。
所以他们想知道,最少需要保留多少根网线?
Input
第一行一个整数T,表示数据组数;
每组数据第一行两个整数N,K,表示总共的机房数目和企鹅数目。
第二行N-1个整数,第i个整数Ai表示机房i+1和机房Ai有一根网线连接(1≤Ai≤i)。
Output
每组数据输出一个整数表示最少保留的网线数目。
Sample Input
2
4 4
1 2 3
4 3
1 1 1
Sample Output
2
2
HINT
对于30%的数据:N≤15;
对于50%的数据:N≤300;
对于70%的数据:N≤2000;
对于100%的数据:2≤K≤N≤100000,T≤10。
Solution
首先,我们最优的选择方案,应该是每隔一条边选一条边,才能最小化使用的边数。
所以先贪心一遍求出上述方案的边数,若这些边已经足以把所有企鹅放下去就直接输出,否则加上多出的企鹅数量(即单独连出来的边)即可。
#include<bits/stdc++.h>
using namespace std;
struct qwq{
int v;
int nxt;
}edge[200001];
int cnt=-1;
int head[200001];
void add(int u,int v){
edge[++cnt].nxt=head[u];
edge[cnt].v=v;
head[u]=cnt;
}
int ans;
bool dfs(int u,int fa){
bool flag=false;
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].v;
if(v==fa)continue;
bool tmp=dfs(v,u);
if(!flag&&tmp){
ans++;
flag=true;
//cout<<u<<" "<<v<<endl;
}
}
return flag?false:true;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
ans=0;
cnt=-1;
memset(head,-1,sizeof(head));
int n,m;
scanf("%d%d",&n,&m);
for(int i=2;i<=n;++i){
int tmp;
scanf("%d",&tmp);
add(i,tmp),add(tmp,i);
}
dfs(1,-1);
//cout<<ans<<endl;
if(ans*2>=m){
if(m&1){
printf("%d\n",m/2+1);
}
else printf("%d\n",m/2);
}
else printf("%d\n",ans+(m-(ans*2)));
}
}