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);
}