在付队的带动下,开始了寒假gym训练,今天第一天训练发现英文水平是限制我拿牌的因素.....
待补
待补
待补
题意:要你构造一棵二叉树,树的叶子节点数刚好=n,要求构造的节点不超过125,且所有子树的左儿子节点数只能=右儿子节点数或者比右儿子节点数刚好多1,且所有儿子节点号小于父亲。
或许题意还是有疑惑,补充一下:假如有个节点5有10个叶子,那么我可以这样构造6号节点:(5,5),表示,节点6的两个儿子都是节点5,这样节点6就有20个叶子,节点号从0开始。
思路:我们发现n最大1e18,但是无关要紧,看到上面这个例子了吗,我们可以实现叶子数指数级增加的构造,我举个例子,假设n=11,那么11的儿子肯定是6,5,那我继续拆6:(3,3),3继续拆:(2,1),2继续拆:(1,1),5同理,直到拆到1结束。代码或许更容易懂。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
map<ll,int>mp;
int l[130],r[130],cnt;
void dfs(ll n)
{
if(mp.count(n))return;
if(n==1)
{
mp[n]=cnt++;
return;
}
ll m=n/2;
dfs(n-m),dfs(m);
mp[n]=cnt++;
l[cnt-1]=mp[n-m];
r[cnt-1]=mp[m];
}
int main()
{
int T;
scanf("%d",&T);
l[0]=-1,r[0]=-1;
while(T--)
{
ll n;
scanf("%lld",&n);
mp.clear();
cnt=0;
dfs(n);
printf("%d\n",cnt);
for(int i=0;i<cnt;i++)printf("%d %d\n",l[i],r[i]);
printf("%d\n",cnt-1);
}
}
待补
题意:求有多少对(x,y),x/y化成最简分数后,x+y<1000
思路:既然是要求最简分数,那我们直接从1到998枚举互质的(x,y),从[ A , B ]求出x倍数区间[ t1, t2 ],从[ C , D ]求出y的倍数区间[ t3, t4 ],答案就加min(t2,t4)-max(t1,t2)+1.
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int gcd(int x,int y)
{
if(!y)return x;
return gcd(y,x%y);
}
int main()
{
ll a,b,c,d,ans=0;
cin>>a>>b>>c>>d;
for(int i=1;i<999;i++)
for(int j=1;j<999;j++)
{
int tmp=gcd(i,j);
if(tmp!=1||i+j>999)continue;
ll t1=a/i,t2=b/i;
if(t1*i<a)t1++;
ll t3=c/j,t4=d/j;
if(t3*j<c)t3++;
ll l=max(t1,t3),r=min(t2,t4);
if(r>=l)
ans+=r-l+1;
}
cout<<ans;
}
题意:有n个点,这n个点是正n边形的顶点,两个人进行博弈,每次每个玩家可以在两个点之间连线(前提条件是这条线不能和已知线相交),哪个玩家先构成凸多边形,就算赢,求先手赢还是后手赢。
思路:假设有n个点,我随机连接两个点 x y,把点集切开成m个点和n-m-2个点,下个玩家要是敢连接某个点和x或者和y,他必输,因此他只能在切开的两个点集连线,那么就转变成了nim博弈,我们枚举所有的 x y 切开的连个点集,通过他们sg函数的异或值求这n个点的sg函数即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn=5005;
int sg[maxn],vis[maxn];
void init()
{
sg[2]=sg[3]=1;
for(int i=4;i<=5000;i++)
{
memset(vis,0,sizeof(vis));
for(int j=0;j<=i-2;j++)
vis[sg[j]^sg[i-j-2]]=1;
for(int j=0;;j++)if(!vis[j])
{
sg[i]=j;
break;
}
}
}
int main()
{
init();
int T,x;
cin>>T;
while(T--)
{
cin>>x;
if(sg[x])puts("First");
else puts("Second");
}
}
思路:定义一段序列严格递减或者非严格递增的长度是m时,那么这段序列前m个元素都是好的,有q次询问,每次询问x表示从第一个元素开始划分子序列,子序列单调性的长度要么大于x,如果子序列单调性长度小于x,那么这段子序列长度就必须是x,除非这段子序列是最后一段。求长度为n的序列能分多少块,且分完块后,有多少不好的元素。
我们先预处理一下,设d[ i ]为从第 i 个元素开始单调性长度,那么对于块大小x,我们可以这样求bad 元素,假设已经遍历到了 i ,如果d[ i ]>=x,那么这段序列单调性长度超过x,全是好的,i 直接跳到 i +d[ i ],如果d[ i ]<x,我们只能跳到 i +x,且这段序列不好的元素有x-d[i],那么可能会有人问,假设1e5次询问分别是x=2,3,4,5,,,,复杂度爆炸了?其实不会,最坏复杂度也就是n/2+n/3+n/4+...+n/q,相当于n*调和级数,调和级数复杂度非常低,应该和对数一个级别,所有不仅不会超时,还快得很.....
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+19;
int n,d[maxn],dp[maxn],a[maxn],ans[maxn],f[maxn];
void init()
{
int p=1,tp;
for(int i=2;i<=n;i++)
if(i==p+1)
{
if(a[i]>=a[i-1])tp=1;
else tp=0;
}
else if(a[i]>=a[i-1]&&!tp)
{
for(int j=p;j<i-1;j++)
d[j]=i-j;
p=i-1;
if(i==n)
{
d[i-1]=2;
d[i]=1;
p=i+1;
break;
}
i--;
}
else if(a[i]<a[i-1]&&tp)
{
for(int j=p;j<i-1;j++)
d[j]=i-j;
p=i-1;
if(i==n)
{
d[i-1]=2;
d[i]=1;
p=i+1;
break;
}
i--;
}
if(p<n)
for(int i=p;i<=n;i++)
d[i]=n+1-i;
}
void work(int x)
{
if(ans[x]!=-1)
{
printf("%d %d\n",f[x],ans[x]);
return;
}
int res=0,p=0;
for(int i=1;i<=n;)
{
res+=d[i],p++;
if(d[i]>=x)
i+=d[i];
else
i+=x;
}
f[x]=p,ans[x]=n-res;
printf("%d %d\n",f[x],ans[x]);
}
int main()
{
int q,x;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),ans[i]=-1;
init();
scanf("%d",&q);
while(q--)
{
scanf("%d",&x);
work(x);
}
}