NOIP提高组模拟赛25

A. 神炎皇

有的时候,不能太相信自己的过去。。。。

这题不是很难,本来打表已经出了最终性质了,但是发现自己曾经交过这道题,于是手贱搞下来对拍一下,发现全挂了。。

输出一下发现好像原来的是错的,但是因为脑抽太过相信自己,认为原来的是AC的。。

最后毅然决然交了过去的码,结果WA0。。。。。。。。。

不要耍小聪明!实践是检验真理的唯一标准!!!

通过打表可以发现规律,现在简单证明一下

枚举gcd(a,b)=d

则令(a+b)d|abd2

(a+b)|abd

因为gcd(a,b)=1

所以aa+bba+b

那么(a+b)ab

所以(a+b)|d

又因为(a+b)d<=n

所以(a+b)<=n

枚举a+b=k,那么合法的d,有n/k2

互质的a,bϕ(k)对(akaka,令b=ka

code
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;

typedef long long ll;
int phi[10000005],prime[10000005],cnt;
bool flag[10000005];
ll n;

int main(){
    scanf("%lld",&n);
    int m=sqrt(n);
    for(int i=2;i<=m;++i){
        if(!flag[i]){
            prime[++cnt]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=m;++j){
            flag[prime[j]*i]=1;
            if(i%prime[j])phi[i*prime[j]]=phi[i]*phi[prime[j]];
            else{
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
        }
    }
    ll ans=0;
    for(int i=2;i<=m;++i)
        ans+=1ll*phi[i]*(n/((ll)i*i));
    printf("%lld\n",ans);
    return 0;
}

B. 降雷皇

没啥说的,就是一个线段树优化DP

code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100005;
const int mod=123456789;
int r[maxn],type,b[maxn],n;
struct node{int mx,sum;node(){mx=sum=0;}};
struct tree{
    node t[maxn<<2|1];
    void push_up(int x){
        int ls=x<<1,rs=x<<1|1;
        t[x].mx=max(t[ls].mx,t[rs].mx);
        t[x].sum=0;
        if(t[ls].mx==t[x].mx)t[x].sum+=t[ls].sum;
        if(t[rs].mx==t[x].mx)t[x].sum+=t[rs].sum;
        t[x].sum%=mod;
    }
    void modify(int x,int l,int r,int pos,int mx,int sum){
        if(l==r){
            if(mx>t[x].mx)t[x].sum=sum;
            else t[x].sum=(sum+t[x].sum)%mod;
            t[x].mx=mx;
            return;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)modify(x<<1,l,mid,pos,mx,sum);
        else modify(x<<1|1,mid+1,r,pos,mx,sum);
        push_up(x);
    }
    node query(int x,int l,int r,int L,int R){
        if(L<=l&&r<=R)return t[x];
        int mid=(l+r)>>1;
        node ansl,ansr;
        ansl.mx=ansr.mx=ansl.sum=ansr.sum=0;
        if(L<=mid)ansl=query(x<<1,l,mid,L,R);
        if(R>mid)ansr=query(x<<1|1,mid+1,r,L,R);
        if(ansl.mx!=ansr.mx)return ansl.mx>=ansr.mx?ansl:ansr;
        ansl.sum=(ansl.sum+ansr.sum)%mod;
        return ansl;
    }
}T;

int main(){
    scanf("%d%d",&n,&type);
    for(int i=1;i<=n;++i)scanf("%d",&r[i]);
    if(type){
        T.modify(1,0,maxn-4,0,0,1);
        for(int i=1;i<=n;++i){
            node x=T.query(1,0,maxn-4,0,r[i]-1);
            T.modify(1,0,maxn-4,r[i],x.mx+1,x.sum);
        }
        node x=T.query(1,0,maxn-4,0,maxn-4);
        printf("%d\n%d\n",x.mx,x.sum);
    }else{
        memset(b,0x3f,sizeof(b));b[0]=0;
	    for(int i=1;i<=n;i++)b[lower_bound(b,b+n+1,r[i])-b]=r[i];
	    int ans=0;for(ans=1;ans<=n;++ans)if(b[ans+1]==b[n+1])break;
        printf("%d\n",ans);
    }
    return 0;
}

C. 幻魔皇

fib的奇妙性质++

以下内容有不少借鉴于动动学长

这个fib树的性质

  1. 每层点数为fib

  2. 从第二层开始,黑点个数为fib

  3. 从第三层开始,白点个数为fib

  4. 对于每一个黑(白)点,如果它有一棵深度为k的子树,那么所有这些子树都是一样的。

为了方便,现在设

w[i]表示第i行白点的个数

b[i]表示第i行黑点的个数

sw[i]表示前i行白点个数

sb[i]表示前i行黑点个数

然后分两种情况

  1. 两个白点的lca为白点

  2. 两个白点的lca为黑点

对于第一种情况,可以发现其中一个白点就是lca

对于一棵根为白色的树,深度为i的白点数显然是w[i+1]

整棵树能够作为这样的树的根的节点一共有sw[ni]

那么ans[i]+=sw[ni]w[i+1]

对于第二种情况,考虑一棵根节点为黑色的树

枚举两侧点到根的距离i,j

(白色子节点的子树中的白点的距离为i,黑色子节点的子树中的白点的距离为j

把黑点到白点的边断开,黑点看成白点,就可以看成两棵根为白色的树

也就是说,白色子节点中距离为i的白点有w[i]个,黑色根及黑色子节点中距离为j的白点有w[j+1]

而这样的子树有sb[nmax(i,j)]

所以ans[i+j]+=sb[nmax(i,j)]w[i]w[j+1]

那么对于这棵树,其对答案的贡献就是w[i]×w[j+1]
,而这样的子树的数量取决与i,j中更大的那一个,所以这样的子树有sb[n−max(i,j)]

code
#include<cstdio>
#include<cstring>
#include<vector>

using namespace std;

const int mod=123456789;
int n,ans[10005];
int w[5005],b[5005],sw[5005],sb[5005];
int main(){
    scanf("%d",&n);
    w[1]=w[3]=1;sw[1]=sw[2]=1;sw[3]=2;
    b[2]=b[3]=1;sb[2]=1;sb[3]=2;
    for(int i=4;i<=n;++i){
        w[i]=(w[i-1]+w[i-2])%mod;
        b[i]=(b[i-1]+b[i-2])%mod;
        sw[i]=(sw[i-1]+w[i])%mod;
        sb[i]=(sb[i-1]+b[i])%mod;
    }
    for(int i=1;i<=n;++i)ans[i]=1ll*sw[n-i]*w[i+1]%mod;
    for(int i=1;i<=n;++i)
      for(int j=1;j<=n;++j)
        ans[i+j]=(ans[i+j]+1ll*sb[n-max(i,j)]*w[i]%mod*w[j+1]%mod)%mod;
    for(int i=1;i<=n+n;++i)printf("%d ",ans[i]);
    return 0;
}
posted @   Chen_jr  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示