【NOIP模拟】 (10.23)

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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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"<

T2

一样远

题目描述:
       一棵树,询问离两个点距离相等的点的数量。

输入格式
第一行一个整数 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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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=0;i--) if(len>=1<=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]=0;i--)
     if(mid>=1<=0;i--) if(len1>=1<=0;i--) if(len2>=1<=0;i--) if(len>=1<>1;
   	 ans=0;
   	 find(x,y,mid,father);
   	 cout<

T3

拆网线

题目描述:
       一棵树,给n-1条边,问最少加入几条边,能使得K个点中任意一个点能与至少另一个点相连。

输入格式
第一行一个整数 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。

解析:
       根据题目,我们可以想到最优的方案为尽量两两配对,最后如果还有剩余的点,则加进来。
       于是,就会出现两种情况:
       1.两两配对的最大配对数sum>=k,ans=(k+1)/2;
       2.sum

       联想到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 
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<

你可能感兴趣的:(NOIP,最近公共祖先(LCA,普通线性DP,模拟)