先说今天考试比较水很多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半小时内写完,后面就有时间打个对拍,应该就会很稳了。