10.22考试总结

先说今天考试比较水很多dalao都Ak了,然后我是真的很想抽自己,下面请看;

1.斐波那契数

    题目大意:给一个数,判断它是不是两个斐波那契数的乘机(n<=100000000);

    感想:很容易看出来f[45],就超1e9了,然后n方枚举就ok了,我日常卡第一题,花了一个半小时,硬是找了个规律,写了个logn的东西,首先可以二分求出f[l]<=n<=f[r],然后你可以发现它们可以是和f[1]的乘积,而这中间的斐波那契数的乘积都一定是由f(l+2-i)*f(i)得来的,这样在(l+2)/2到l枚举,因为它不超过45,所以一半大概是20几,差不多也是log级别的,所以玄学的花了1个半小时写出来,我也是醉了。

# include <iostream>
# include <cstdio>
# include <cstring>
# include <algorithm>
# include <cmath>
# include <list>
using namespace std;
typedef long long ll;
ll read()
{
	ll f=1,i=0;char ch=getchar();
	while(ch<'0'||ch>'9') 
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		i=(i<<3)+(i<<1)+ch-'0';
		ch=getchar();
	}
	return f*i;
}
ll f[1000],x;
int main()
{
	
	f[0]=0;f[1]=1;
	for(int i=2;i<=100;++i) 
        f[i]=f[i-1]+f[i-2];
	int t=read();
	while(t--)
	{
		x=read();
		int l=1,r=50;
		while(l<=r)
		{
			int mid=l+r>>1;
			if(f[mid]<=x) l=mid+1;
			else r=mid-1;
		}
		int L,R,flag=0;
		if(x==f[l]||x==f[r]) flag=1;
		R=r-1;
		if((2+r)&1) L=(2+r)/2+1;
		else L=(2+r)/2;
		for(int i=L;i<=R;++i)
		    if(f[i]*f[r+2-i]==x) flag=1;
		if(flag) 
		{
			puts("Yes");
		}
		else puts("No");
	}
}
2.一样远

   题目大意:给一棵树,在给两个点a,b,求树上到它们距离相等的点有多少个。

   感想:因为第一题的智障,我还剩两个小时刚后两道题,然后这一道题花了50分钟写完,lca+倍增嘛,过了样例以及手造的几组大样例,感觉很稳,因为时间不够也懒得打对拍了,于是成功wa的只剩十分。正解:求lca,然后倍增找到中点,中点中含有a,b两点的子树上的点都不能选于是减去,剩下的就是答案,注意lcd就是中点的时候要特判,而且是有可能两个点一个在上一个在下,或是两个都在下,别问我怎么知道的,说来都是qaq。

# include <iostream>
# include <cstdio>
# include <cstring>
# include <algorithm>
# include <cmath>
# include <list>
using namespace std;
typedef long long ll;
int read()
{
	int f=1,i=0;char ch=getchar();
	while(ch<'0'||ch>'9') 
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		i=(i<<3)+(i<<1)+ch-'0';
		ch=getchar();
	}
	return f*i;
}
const int N=100005;
struct node
{
	int v;
	node *next;
}E[N<<2],*first[N],*cnt=E;
int n,m,x,y,z,q,lenth,son;
int dis[N],dep[N],fa[N][20],size[N];
bool vis[N]={false};
inline void AddEdge(int x,int y)
{
	node *p=++cnt;
	p->next=first[x];
	first[x]=p;p->v=y;
}
inline void DFS(int u)
{
	vis[u]=true;size[u]=1;
	for(node *p=first[u];p;p=p->next)
	{
		if(!vis[p->v])
		{
			dis[p->v]=dis[u]+1;
			dep[p->v]=dep[u]+1;
			fa[p->v][0]=u;
			DFS(p->v);
			size[u]+=size[p->v];
		}
	}
}
inline void pre()
{
	for(int j=1;j<=17;++j)
	    for(int i=1;i<=n;++i)
	        if(fa[i][j-1]) fa[i][j]=fa[fa[i][j-1]][j-1];
}
int LCA(int x,int y)
{
	int len=dep[x]-dep[y];
	for(int i=17;i>=0;--i)
	{
		if(len>=(1<<i))
		{
			len-=(1<<i);
			x=fa[x][i];
		}
	}
	if(x==y) return x;
	for(int i=17;i>=0;--i)
	{
		if(fa[x][i]!=fa[y][i])
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}
int update(int len,int x)
{
	for(int i=17;i>=0;--i)
	{
		if(len>=(1<<i))
		{
			len-=(1<<i);
			x=fa[x][i];
		}
	}return x;
}
int main()
{
	
	n=read();
	for(int i=1;i<n;++i)
	{
		x=read(),y=read();
		AddEdge(x,y),AddEdge(y,x);
	}
	DFS(1);pre();
	m=read();
	while(m--)
	{
		x=read(),y=read();
		if(x==y)
		{
			printf("%d\n",n);
			continue;
		}
		if(dep[x]<dep[y])swap(x,y);
		int lca=LCA(x,y);son=0;
		lenth=dis[x]+dis[y]-2*dis[lca];
		if(lenth&1) printf("0\n");
		else
		{
			lenth>>=1;int fx,fy;
			fx=update(lenth-1,x);
			fy=update(lenth-1,y);
			if(dep[x]-dep[lca]==lenth) 
			{
				cout<<n-size[fx]-size[fy]<<endl;
				continue;
			}
			cout<<size[fa[fx][0]]-size[fx]<<endl;
		}
	}
}
3.拆网线

    题目大意:给一棵树,给一个点数k,要求保证k点中一个点至少与另一个点有一条边,求最少保留几条边;

    感想:因为前两道题的智障,于是想了会,发现就是一个贪心嘛,两两配对下来一定最优,于是拓扑排序,每次选入度最小的与它对面的点匹配,走一遍就行了,于是复杂度o(n),输出时如果要求的点数大于两个点的对数(num)乘2,则输出k-num*2+num,若大于则输出(k+1)/2,而我智障的直接输出了num,我可能是吃了假药吧。

# include <iostream>
# include <cstdio>
# include <cstring>
# include <algorithm>
# include <cmath>
# include <list>
# include <queue>
using namespace std;
typedef long long ll;
int read()
{
	int f=1,i=0;char ch=getchar();
	while(ch<'0'||ch>'9') 
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		i=(i<<3)+(i<<1)+ch-'0';
		ch=getchar();
	}
	return f*i;
}
const int N=100005;
struct node
{
	int u,v,next;
}E[N<<2];
int t,n,m,x,y,k,cnt,r[N],mark[N],first[N];
inline void initial()
{
     cnt=0;
     memset(mark,0,sizeof(mark));
     memset(r,0,sizeof(r));
     memset(first,0,sizeof(first));
     memset(E,0,sizeof(E));
}
inline void AddEdge(int x,int y)
{
    E[++cnt].next=first[x];
    first[x]=cnt;
    E[cnt].v=y;
    r[x]++,r[y]++;
}
int main()
{
        t=read();
	while(t--)
	{
		 initial();
		 n=read(),k=read();
		 for(int i=1;i<n;++i)
		 {
		 	x=read();
		 	AddEdge(x,i+1);
		 	AddEdge(i+1,x);
		 }
		 queue<int>q;
		 for(int i=1;i<=n;++i) if(r[i]==2) q.push(i);
		 while(!q.empty())
		 {
		 	int now=q.front();q.pop();
		 	for(int i=first[now];i;i=E[i].next)
		 	{
		 		int v=E[i].v;
				if(!mark[now]&&!mark[v])
		 		    mark[now]=1,mark[v]=2;
		 		r[v]-=2;if(r[v]==2) q.push(v);
		 	}
		 }
		 int num=0;
		 for(int i=1;i<=n;++i) if(mark[i]==1) num++;
		 if(k<=num*2) printf("%d\n",(k+1)/2);
		 else printf("%d\n",k-num*2+num);
	}
}

总结:t1日常被卡,我真的是个妖怪,要注意别想太复杂了,毕竟是t1,然后只要t1半小时内写完,后面就有时间打个对拍,应该就会很稳了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值