NOIP 模拟 七十五

T1 如何优雅的送分

首先考虑转化\(2^{f_n}\)

\[2^{f_i}=\sum_ {d \mid i}u_i^2 \]

这个考虑莫比乌斯函数的性质,加上组合意义得到。

\[\sum_ {d \mid i}u_i^2=\sum_ {d\mid i}\sum_{k^2\mid d}u_k \]

这个可以用二项式定理证明。

然后做个前缀。

\[ans=\sum_ {i=1}^n\sum_ {d \mid i}\sum_{k^2\mid d}u_k \]

转变求和顺序

\[ans=\sum_ {k=1}^nu_k\sum_ {k^2\mid d}\lfloor \frac{n}{d}\rfloor \]

转变枚举含义

\[ans=\sum_ {k=1}^n u_k\sum_ {i=1}^{\lfloor \frac{n}{k^2}\rfloor} \lfloor \frac{n}{k^2\times i}\rfloor \]

发现后半部分可以数论分块。复杂度\(O(\sqrt{n}lgn)\)

#include<bits/stdc++.h>
#define N 1000001
#define int long long
#define mod 1000000007
using namespace std;
int u[N],su[N],cnt,n,m,ans;
bool bo[N];
inline int solve(int n)
{   int tmp=0;
    for(int l=1,r;l<=n;l=r+1)
    {   r=n/(n/l);
        tmp=(tmp+(r-l+1)%mod*(n/l%mod)%mod)%mod;
    }
    return tmp;
}
signed main()
{   freopen("elegant.in","r",stdin);
    freopen("elegant.out","w",stdout);
    scanf("%lld",&n);m=sqrt(n);
    u[1]=1;bo[1]=1;
    for(int i=2;i<=m;++i)
    {   if(!bo[i])su[++cnt]=i,u[i]=-1;
        for(int j=1;j<=cnt and su[j]*i<=m;++j)
        {   bo[i*su[j]]=1;u[i*su[j]]=-u[i];
            if(i%su[j]==0){u[i*su[j]]=0;break;}
        }
    }
    for(int i=1;i<=m;++i)if(u[i])ans=(ans+u[i]*solve(n/(i*i))+mod)%mod;
    printf("%lld\n",ans);
}

T2 阴阳

暴力水掉了,不多说了,回头补坑点分树。

#include<bits/stdc++.h>
#define N 200001
using namespace std;
int type,n,m,head[N],now,t,dep[N],cnt,tot,ans,son[N],top[N],fa[N],siz[N];
unordered_set<int>st[N];
struct kk
{int nxt,to;}bian[N<<1];
inline void add(int u,int v)
{   bian[++tot].to=v;
    bian[tot].nxt=head[u];
    head[u]=tot;
}
inline void dfs1(int x,int f)
{   fa[x]=f;dep[x]=dep[f]+1;siz[x]=1;son[x]=-1;
    for(int i=head[x];i;i=bian[i].nxt)
    {   int v=bian[i].to;
        if(v==f)continue;
        dfs1(v,x);
        siz[x]+=siz[v];
        if(son[x]==-1 or siz[son[x]]<siz[v])son[x]=v;
    }
}
inline void dfs2(int x,int t)
{   top[x]=t;
    if(son[x]!=-1)dfs2(son[x],t);
    for(int i=head[x];i;i=bian[i].nxt)
    {   int v=bian[i].to;
        if(v==fa[x] or v==son[x])continue;
        dfs2(v,v);    
    }
}
inline int LCA(int x,int y)
{   int tx=x,ty=y;
    while(top[x]!=top[y])
    {   if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    int lca=dep[x]<dep[y]?x:y;
    return dep[tx]+dep[ty]-2*dep[lca];
}
signed main()
{   freopen("yygq.in","r",stdin);   
    freopen("yygq.out","w",stdout);
    scanf("%d%d",&type,&n);
    for(int i=1;i<n;++i)
    {   int a,b;scanf("%d%d",&a,&b);
        add(a,b);add(b,a);
    }
    dfs1(1,0);dfs2(1,1);
    scanf("%d",&m);
    for(int i=1;i<=m;++i)
    {   int tp,x;scanf("%d%d",&tp,&x);
        x=x^(ans*type);
        if(tp==1)
        {   ++t;
            st[t]=st[now];
            if(st[t].find(x)!=st[t].end())st[t].erase(st[t].find(x));
            else st[t].insert(x);
            now=t;

        }
        if(tp==2)
        {   ans=1000000000;
            for(auto j:st[now])ans=min(ans,LCA(x,j));
            printf("%d\n",ans);
        }
        if(tp==3)
        {now=x;}
    }
}

T3 你猜是不是找规律

可以把还原 k 步变为加密 k 步。

得到dp :

\[dp_{n,k}=dp_{n-1,k}+dp_{n-1,k-1}\times (n-1) \]

含义显然,不再多说。归纳得到 ans 为 2k 次多项式。拉格朗日插值得到答案。

#include<bits/stdc++.h>
#define mod 1000000007
#define int long long
using namespace std;
int dp[2][6101],k;
long long n,ans=0,x[60001],y[60001],sum[60001];
inline long long  qpow(long long a,long long b){long long base=1;while(b){if(b&1)base=base*a%mod;a=a*a%mod;b>>=1;}return base;}
inline long long lagrange(long long n,long long  *x,long long  *y,long long  xi)
{	long long ans=0;
	for(int i=0;i<=n;++i)
	{	long long  s1=1,s2=1;
		for(int j=0;j<=n;++j)
		if(i!=j)
		{	s1=s1*(((xi-x[j])%mod+mod)%mod)%mod;
			s2=s2*(((x[i]-x[j])%mod+mod)%mod)%mod;
		}
		ans=(ans+y[i]%mod*s1%mod*qpow(s2,mod-2)%mod)%mod;
	}
	return (ans+mod)%mod;
}
signed main()
{   freopen("guess.in","r",stdin);
    freopen("guess.out","w",stdout);
    scanf("%lld%lld",&n,&k);
    dp[0][0]=1;
    for(int i=1;i<=2*k+1;++i)
    {   int now=i%2;x[i-1]=i;
        dp[now][0]=1;
        for(int j=1;j<=k;++j)
        {   dp[now][j]=dp[now^1][j]+(1ll*(i-1)*dp[now^1][j-1]%mod)%mod;
            if(j<=k)y[i-1]=(y[i-1]+dp[now][j])%mod;
        }
        y[i-1]=(y[i-1]+1)%mod;
    }
    printf("%lld\n",lagrange(2*k,x,y,n));
}

T4

#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
int n,dd[555],maxn,a,b,biao,sum,id,lim;
int bo[4444444],tmp[4444444];
signed main()
{   freopen("novel.in","r",stdin);
    freopen("novel.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&dd[i]),sum+=dd[i];
    bo[0]=1;int shu=0;
    for(int i=1;i<=n;++i)for(int j=sum-dd[i];j>=0;--j)if(bo[j])bo[j+dd[i]]=(bo[j+dd[i]]+bo[j])%mod;
    for(int i=1;i<=n;++i)
    {   int ji=0;for(int j=dd[i]-1;j>=0;--j)tmp[j]=bo[j];
        for(int j=dd[i];j<=sum;++j)
        {   tmp[j]=bo[j];
            if(tmp[j])tmp[j]=(tmp[j]-tmp[j-dd[i]]+mod)%mod;
        }
        for(int j=1;j<=sum;++j)if(tmp[j])++ji;
        if(ji>maxn)maxn=ji,a=dd[i],id=i;
        else if(ji==maxn and dd[i]<a)a=dd[i],id=i;
    }
    maxn=0;memset(bo,0,sizeof(bo));bo[sum]=1;
    for(int i=1;i<=n;++i)
    {   if(id==i)continue;
        for(int j=sum*2-dd[i];j>=0;--j)if(bo[j])bo[dd[i]+j]=1;
        for(int j=dd[i];j<=sum*2;++j)if(bo[j])bo[j-dd[i]]=1;
    }
    for(int i=sum+1;i<=2*sum;++i)if(!bo[i]){b=i-sum;break;}
    printf("%d %d\n",a,b);
}
posted @ 2021-10-12 17:16  -zxb-  阅读(57)  评论(0)    收藏  举报