NOIP提高组模拟赛25

A. 神炎皇

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

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

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

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

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

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

枚举\(gcd(a,b)=d\)

则令\((a'+b')d|a'b'd^2\)

\((a'+b')|a'b'd\)

因为\(gcd(a',b')=1\)

所以\(a'\perp a'+b'\)\(b'\perp a'+b'\)

那么\((a'+b')\perp a'b'\)

所以\((a'+b')|d\)

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

所以\((a'+b')<=\sqrt n\)

枚举\(a'+b'=k\),那么合法的\(d\),有\(n/k^2\)

互质的\(a',b'\)\(\phi(k)\)对(\(a' \perp k\)\(a' \perp k-a'\),令\(b'=k-a'\)

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[n-i]\)

那么\(ans[i]+=sw[n-i]*w[i+1]\)

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

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

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

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

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

而这样的子树有\(sb[n−max(i,j)]\)

所以\(ans[i+j]+=sb[n-max(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 @ 2022-05-15 16:32  Chen_jr  阅读(36)  评论(0编辑  收藏  举报