T1
fibonacci
题目描述:
判读一个数是否为两个斐波拉契数的乘积。
Fibonacci 数的定义:F0=0,F1=1,Fk=Fk-1+Fk-2 。
输入格式
第一行一个整数 T 代表提问次数。
接下来 T 行,每行一个数字 A 表示询问的数。
输出格式
对于每次提问,如果这个数可以被分解成两个 Fibonacci 数的成绩输出“Yes”,否则输出“No”。
备注
【数据范围】
对于 50% 的数据:A≤50;
对于 100% 的数据:T≤100;0≤A≤10^9 。
解析:
一道看似简单却暗藏杀机的模拟题。
因为斐波那契数列的增长速度是很快的,所以可以先打表找出最大的值。
由于数据最大为10^9,所以只用找到刚好超过最大值的数就行了。
注意数组用long long
代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <cctype>
#include <queue>
using namespace std;
long long sum[50];
int n,t;
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
int main()
{
//freopen("fib.in","r",stdin);
//freopen("fib.out","w",stdout);
sum[1]=1;
for(int i=2;i<=45;i++) sum[i]=sum[i-1]+sum[i-2];
t=get_int();
while(t--)
{
int x=0;
n=get_int();
for(int i=0;i<=45;i++)
{
for(int j=0;j<=45;j++)
if((sum[i]*sum[j])==n) {x=1;break;}
if(x==1) break;
}
if(x==1) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
T2
一样远
题目描述:
一棵树,询问离两个点距离相等的点的数量。
输入格式
第一行一个整数 N,代表点的个数。
接下来 N-1 行,每行两个数字 F 和 T ,表示点F 和点 T 之间有一条道路。
接下来一行一个整数 M 代表询问次数。
接下来 M 行,每行两个数字 A 和 B ,表示这次询问的点 A 和点 B(A可能与B相同)。
第一行一个整数 N,代表点的个数。
接下来 N-1 行,每行两个数字 F 和 T ,表示点F 和点 T 之间有一条道路。
接下来一行一个整数 M 代表询问次数。
接下来 M 行,每行两个数字 A 和 B ,表示这次询问的点 A 和点 B(A可能与B相同)。
输出格式
输出 M 行,每行一个整数表示到 A 和 B 一样远的点个数。
备注
【数据范围】
对于 30% 的数据:N,M≤1000;
对于另外 10% 的数据:A=B;
对于另外 30% 的数据:保证树的形态随机;
对于 100% 的数据:1≤N,M≤100000。
解析:
很明显能看出来这是一道LCA模板题,但还是要对LCA比较熟悉才可能在考场上AC。
基本思路如下:
首先,我们先找到这两个点的LCA。
然后,再得到两点的距离,求出中点。
最后,求出与中点相连但不与这两点相连的点数即可
LCA如何求在这里就不说了,这道题的关键在最后一步。
我们可以定义一个数组size,size[i]表示以i为根的子树的总数(包括i)。size可以在进行询问操作前dfs求得。
于是最后的答案要分两种情况进行讨论:
1.两点的LCA就是中点 ans=n-size[x]-size[y] (x,y为这两点距离LCA最近的儿子)
2.两点的LCA不是中点 ans=size[midpoint]-size[x] (x为两点中深度最大的点)
代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <cctype>
#include <queue>
using namespace std;
const int Max=100100;
int n,m,s,ans,head,tail;
int first[Max],depth[Max],point[Max];
int f[Max][25],exist[Max];
struct shu{int to,next;};
shu bian[Max*4];
int size[Max],to[Max];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int x,int y) //建边
{
s++;
bian[s].next=first[x];
first[x]=s;
bian[s].to=y;
}
inline void pre()
{
memset(exist,0,sizeof(exist));
head=0;
tail=1;
point[1]=1;
depth[1]=0;
exist[1]=1;
while(head<tail)
{
int from=point[++head];
for(register int u=first[from];u;u=bian[u].next)
if(exist[bian[u].to]==0)
{
exist[bian[u].to]=1;
point[++tail]=bian[u].to;
depth[bian[u].to]=depth[from]+1;
f[bian[u].to][0]=from;
}
}
}
inline int LCA(int x,int y)
{
if(depth[x]<depth[y]) swap(x,y);
int len=depth[x]-depth[y];
for(register int i=20;i>=0;i--) if(len>=1<<i) {x=f[x][i];len-=1<<i;}
if(x==y) return x;
for(register int i=20;i>=0;i--) if(f[x][i]!=f[y][i]) {x=f[x][i];y=f[y][i];}
return f[x][0];
}
inline void go(int point) //计算size
{
size[point]=1;
for(int u=first[point];u;u=bian[u].next)
if(bian[u].to!=f[point][0])
{
go(bian[u].to);
size[point]+=size[bian[u].to];
}
}
inline void find(int x,int y,int mid,int father)
{
int midpoint;
if(depth[x]<depth[y]) swap(x,y);
midpoint=x;
for(register int i=20;i>=0;i--)
if(mid>=1<<i){mid-=1<<i;midpoint=f[midpoint][i];}
if(midpoint==father)
{
int len1=depth[x]-depth[midpoint]-1;
int len2=depth[y]-depth[midpoint]-1;
for(int i=20;i>=0;i--) if(len1>=1<<i) {len1-=1<<i;x=f[x][i];}
for(int i=20;i>=0;i--) if(len2>=1<<i) {len2-=1<<i;y=f[y][i];}
ans=n-size[x]-size[y];
}
else
{
int len=depth[x]-depth[midpoint]-1;
for(int i=20;i>=0;i--) if(len>=1<<i) {len-=1<<i;x=f[x][i];}
ans=size[midpoint]-size[x];
}
}
int main()
{
// freopen("equal.in","r",stdin);
//freopen("equal.out","w",stdout);
n=get_int();
for(register int i=1;i<=n-1;i++)
{
int x=get_int();
int y=get_int();
build(x,y);
build(y,x);
}
pre();
for(register int i=1;i<=20;i++)
for(register int j=1;j<=n;j++)
if(f[j][i-1]!=0) f[j][i]=f[f[j][i-1]][i-1];
go(1);
m=get_int();
for(register int i=1;i<=m;i++)
{
int x=get_int(),y=get_int();
if(x==y) {cout<<n<<endl;continue;} //若x==y,则所有的点都满足
int father=LCA(x,y),len=depth[x]+depth[y]-2*depth[father];
if(len&1) {cout<<"0"<<endl;continue;} //若距离为奇数当然没有中点
int mid=len>>1;
ans=0;
find(x,y,mid,father);
cout<<ans<<endl;
}
return 0;
}
T3
拆网线
题目描述:
一棵树,给n-1条边,问最少加入几条边,能使得K个点中任意一个点能与至少另一个点相连。
输入格式
第一行一个整数 T ,表示数据组数;
每组数据第一行两个整数 N,K 。
第二行 N-1 个整数,第 i 个整数 Ai 表示i+1 和 Ai 有一根边连接(1≤Ai≤i)。
输出格式
每组数据输出一个整数表示最少加入的边数。
第一行一个整数 T ,表示数据组数;
每组数据第一行两个整数 N,K 。
第二行 N-1 个整数,第 i 个整数 Ai 表示i+1 和 Ai 有一根边连接(1≤Ai≤i)。
输出格式
每组数据输出一个整数表示最少加入的边数。
备注
【数据范围】
对于 30% 的数据:N≤15;
对于 50% 的数据:N≤300;
对于 70% 的数据:N≤2000;
对于 100% 的数据:2≤K≤N≤100000,T≤10。
【数据范围】
对于 30% 的数据:N≤15;
对于 50% 的数据:N≤300;
对于 70% 的数据:N≤2000;
对于 100% 的数据:2≤K≤N≤100000,T≤10。
解析:
根据题目,我们可以想到最优的方案为尽量两两配对,最后如果还有剩余的点,则加进来。
于是,就会出现两种情况:
1.两两配对的最大配对数sum>=k,ans=(k+1)/2;
2.sum<k,ans=sum+(k-sum*2);
联想到DP,定义一个数组f[i][0/1]
f[i][0]表示以i为根的子节点的最大配对数(不包括i)
f[i][1]表示以i为根的子节点的最大配对数(包括i)
得到状态转移方程:
f[u][0]=∑f[v][1];
f[u][1]=max(f[u][1],f[u][0]-f[v][1]+f[v][0]+1);
解释一下第二个转移方程,f[u][0]-f[v][1]+f[v][0]+1表示的是u若与v相连得到的最大配对数。因为f[u][0]=∑f[v][1],所以该方程的意思是从f[u][0]中去掉v这个点再与u相连。
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=100100;
int t,n,k,s;
int f[Max][5];
int first[Max*2];
struct shu{int to,next;};
shu bian[Max*2];
bool exist[Max*2];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
void clean()
{
s=0;
memset(exist,0,sizeof(exist));
memset(f,0,sizeof(f));
memset(bian,0,sizeof(bian));
memset(first,0,sizeof(first));
}
inline void build(int x,int y)
{
s++;
bian[s].next=first[x];
first[x]=s;
bian[s].to=y;
}
inline void dfs(int point,int father)
{
int sum=0;
if(exist[point]) return;
exist[point]=1;
for(register int u=first[point];u;u=bian[u].next)
{
if(bian[u].to!=father)
{
dfs(bian[u].to,point);
f[point][0]+=max(f[bian[u].to][0],f[bian[u].to][1]);
sum+=f[bian[u].to][1];
}
}
for(register int u=first[point];u;u=bian[u].next)
if(bian[u].to!=father)
f[point][1]=max(f[point][1],sum-f[bian[u].to][1]+f[bian[u].to][0]+1);
}
int main()
{
// freopen("tree.in","r",stdin);
//freopen("lx.out","w",stdout);
t=get_int();
while(t--)
{
int ans=-1;
clean();
n=get_int();
k=get_int();
for(register int i=1;i<=n-1;i++)
{
int x=get_int();
build(i+1,x);
build(x,i+1);
}
dfs(1,-1);
ans=max(f[1][0],f[1][1]);
if(ans*2>=k) cout<<(k+1)/2<<endl;
else cout<<ans+k-(ans*2)<<endl;
}
return 0;
}