多校冲刺 NOIP 20211030 模拟 (20)
T1 集合均值
送分题一个,好像线性筛求逆元比递推要快????因为我根本没有卡常来着。。。。
#include<bits/stdc++.h>
#define niuma (n*m+1)
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int maxn=1e5+5;
const int mod=998244353;
int n,a[maxn],m,inv[20000007],pr[20000007],cnt;
bool bo[20000007];
inline int ksm(int x,int y)
{
int res=1;x=x%mod;
for(;y;y>>=1){if(y&1)res=res*x%mod;x=x*x%mod;}
return res;
}
inline void shai()
{
inv[1]=1;
for(int i=2;i<=niuma;i++)
{
if(!bo[i]){inv[i]=ksm(i,mod-2);pr[++cnt]=i;}
for(int j=1;j<=cnt&&i*pr[j]<=niuma;j++)
{
bo[i*pr[j]]=1;
inv[i*pr[j]]=inv[i]*inv[pr[j]]%mod;
if(!(i%pr[j])) break;
}
}
}
signed main()
{
freopen("mos.in","r",stdin);
freopen("mos.out","w",stdout);
n=read();m=read();shai();
for(int i=1;i<=n;i++) a[i]=read();
int sum=0,iv=ksm(n*m,mod-2);
for(int i=n*m+1;i>=1;i--) {inv[i]=(inv[i+1]+inv[i])%mod;}
for(int i=1;i<=n*m;i++) sum=(sum+(inv[i+1])%mod*iv%mod)%mod;
int ans=0; for(int i=1;i<=n;i++)
ans=(ans+a[i]%mod*sum%mod)%mod;
cout<<ans*m%mod<<endl;
}
T2 聚烷撑乙二醇
这道题证明了我期望学的跟没学一样,期望明明要逆推,我正着推了一亿年没整出来,我就是个sb
我们设F[i]为只用第i个到第n个的期望数字是多少,很显然f[n]就是(r[i]+l[i])/2
然后从f[i+1]轻松得到f[i],就是看取出来一个数假如比f[i+1]小,那么就接着选后面的
否则就在大于f[i+1]到r[i]里面均匀分配(取平均值)
#include<bits/stdc++.h>
#define double long double
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int maxn=1e6+5;
const double eps=1e-9;
int n;
double l[maxn],r[maxn];
inline double ksm(double x,int y)
{
double res=1;
while(y){if(y&1)res=res*x;x=x*x;y>>=1;}
return res;
}
double f[maxn];
signed main()
{
freopen("pag.in","r",stdin);
freopen("pag.out","w",stdout);
n=read();bool flag=0;
for(int i=1;i<=n;i++)
{
l[i]=read();
r[i]=read();
if(l[i]!=r[i])flag=1;
}
if(!flag)
{
double ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,l[i]);
printf("%.5Lf",(double)ans);
return 0;
}
else
{
f[n]=(l[n]+r[n])/2.0;
for(int i=n-1;i>=1;i--)
{
double len=r[i]-l[i];
if(f[i+1]>=r[i]) f[i]=f[i+1];
else if(f[i+1]<=l[i]) f[i]=(r[i]+l[i])/2.0;
else f[i]=((f[i+1]-l[i])/len)*f[i+1]+((r[i]+f[i+1])/2)*(r[i]-f[i+1])/len;
}
printf("%.5Lf",f[1]);
}
}
T3 技术情报局
首先想到的是一个分治的做法,主要是因为昨天做了这个题目,而且也比较好想,毕竟还要打对拍,就顺便写了下
先说一下分治的写法,首先对于区间l~r,我们的最大值不是在左边就是在右边;
我们可以强制让最大值在一侧,然后另一侧用单调指针扫着,然后顺便统计下答案
代码如下:
inline void msort(int l,int r)
{
if(l==r){return ans=(ans+a[l]*a[l])%mod,void();}
int mid=(l+r)>>1;msort(l,mid);msort(mid+1,r);
int tmp=mid,maxx=0,sum=0,nw=1,wo=1;
for(int i=mid;i>=l;i--)
{
maxx=max(a[i],maxx);wo=wo*a[i]%mod;
while(a[tmp+1]<maxx&&tmp<r)
{
tmp++;nw=nw*a[tmp]%mod;
sum=(sum+nw)%mod;
}
ans=(ans+maxx*wo%mod*sum%mod)%mod;
}
tmp=mid+1,maxx=0,sum=0,nw=1,wo=1;
for(int i=mid+1;i<=r;i++)
{
maxx=max(a[i],maxx);wo=wo*a[i]%mod;
while(a[tmp-1]<=maxx&&tmp>l)
{
tmp--;nw=nw*a[tmp]%mod;
sum=(sum+nw)%mod;
}
ans=(ans+maxx*wo%mod*sum%mod)%mod;
}
}
写着写着,我发现很多地方统计是重复的,偶然间想起来笛卡尔树,因为笛卡尔树上的元素以每个点为最大值的区间是包含关系的
所以我们可以将分治里面的合并转化为笛卡尔树里的子树进行合并
我们设val[x][1]为x管辖的区间全部贴紧右边界的区间乘积的和,val[x][0]是左边的
那么我们合并起来就会非常好写了
ans=(ans+a[x]*a[x])%mod;val[x][0]=val[x][1]=a[x];sum[x]=a[x];
if(ls)
{
dfs(ls);
ans=(ans+(val[ls][1]*val[x][0]%mod*a[x]%mod))%mod;
val[x][0]=(sum[ls]*val[x][0]+val[ls][0])%mod;
val[x][1]=(sum[x]*val[ls][1]+val[x][1])%mod;
sum[x]=sum[x]*sum[ls]%mod;
}
if(rs)
{
dfs(rs);
ans=(ans+(val[rs][0]*val[x][1]%mod*a[x]%mod))%mod;
val[x][0]=(val[rs][0]*sum[x]+val[x][0])%mod;
val[x][1]=(val[x][1]*sum[rs]+val[rs][1])%mod;
sum[x]=sum[x]*sum[rs]%mod;
}
不过一开始以为内存只有512兆,担心会爆掉,所以写了个l==r的特判,结果内存开了2个G。。。
T4 肯德基
我是筛法超级弱者。。。。。
由于我太弱了,完全忘记了莫比乌斯函数就是天然的容斥系数,于是就爆砍30分。。。
n的范围是1e14,显然线性筛只需要筛的根号,然后容斥一下就好了。。。
\[ans=\sum\limits_{i=1}^{\sqrt{n}}\mu{(i)}i^2S(\left \lfloor \frac{n}{i^2} \right \rfloor)
\]
\[s(i)=\sum\limits_{j=1}^{j<=i}j
\]
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int maxn=2e7+5;
bool bo[maxn];
int pr[maxn],mu[maxn],cnt;
int sum[maxn];
inline ull f(ull x)
{
if(x&1) return (x+1)/2*x;
else return x/2*(x+1);
}
inline void shai()
{
mu[1]=1;
for(int i=2;i<=1e7;i++)
{
if(!bo[i]){pr[++cnt]=i;mu[i]=-1;}
for(int j=1;j<=cnt&&i*pr[j]<=1e7;j++)
{
bo[i*pr[j]]=1;
if(!(i%pr[j])){mu[i*pr[j]]=0;break;}
mu[i*pr[j]]=-mu[i];
}
}
for(int i=1;i<=1e7;i++) sum[i]=sum[i-1]+mu[i]*i*i;
}
signed main()
{
freopen("kfc.in","r",stdin);
freopen("kfc.out","w",stdout);
shai();int t=read();
while(t--)
{
int n=read();ull ans=0;
for(int l=1,r;l*l<=n;l=r+1)
{
r=(int)sqrt(n/(n/(l*l)));
ans+=(sum[r]-sum[l-1])*(int)f(n/(l*l));
}
cout<<ans<<endl;
}
}