今天好像全是简单题,但仍然没想到多少正解,考完一个半小时就改完了。。
对各种知识点还是没有理解透,一些套路想不出来
\(\Huge{\color{green}{菜}}\)
T1 集合均值
列出贡献发现每个元素贡献的概率为
\[\frac{1}{n\times m}\sum_{i=1}^{n\times m} \frac{i}{i+1} \]那么答案为
\[(m\times\sum_{i=1}^nw_i)\times\frac{1}{n\times m}\sum_{i=1}^{n\times m} \frac{i}{i+1} \]线性求逆元
\[inv_i\equiv \left\lfloor \frac{p}{i}\right\rfloor\times inv_{p\;mod\;i} (mod\;p) \]或直接线性筛逆元。
\(code:\)
T1
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL 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;
}
void write(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=20000010,mod=998244353;
int n,m,tot,sum,base;
int cnt,pri[NN],inv[NN];
bool vis[NN];
int qpow(int a,int b){
int res=1;
for(;b;b>>=1){
if(b&1) res=res*a%mod;
a=a*a%mod;
}
return res;
}
void getinv(){
inv[0]=inv[1]=1;
for(int i=2;i<=tot+1;i++){
if(!vis[i]) pri[++cnt]=i,inv[i]=qpow(i,mod-2);
for(int j=1;j<=cnt&&pri[j]*i<=tot;j++){
vis[pri[j]*i]=1;
inv[pri[j]*i]=inv[i]*inv[pri[j]]%mod;
if(i%pri[j]==0) break;
}
}
}
signed main(){
freopen("mos.in","r",stdin);
freopen("mos.out","w",stdout);
n=read(); m=read(); tot=n*m;
getinv();
for(int i=1;i<=n;i++) sum+=read();
sum=sum%mod*m%mod;
for(int i=1;i<=tot;i++)
(base+=i*inv[i+1])%=mod;
sum=sum*inv[tot]%mod;
sum=sum*base%mod;
write(sum,'\n');
return 0;
}
T2 聚烷撑乙二醇
期望要逆推。然后没了
对于一个生成器,若它生成的数小于它之后的期望,那么不取它,否则取它。
最后一个生成器期望为\(\frac{l_i+r_i}{2}\),第一个生成器的期望为答案。
\(code:\)
T2
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL;
typedef long double DB;
int read(){
LL 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;
}
void write(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=1000010;
int n;
DB f[NN],l[NN],r[NN];
signed main(){
freopen("pag.in","r",stdin);
freopen("pag.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
l[i]=read(),r[i]=read();
f[n]=(l[n]+r[n])/2;
for(int i=n-1;i;i--){
if(f[i+1]<=l[i]){ f[i]=(l[i]+r[i])/2.0; continue; }
else if(f[i+1]>=r[i]){ f[i]=f[i+1]; continue; }
f[i]=(f[i+1]-l[i])*f[i+1]/(r[i]-l[i])+(r[i]-f[i+1])*(f[i+1]+r[i])/2.0/(r[i]-l[i]);
}
printf("%.5Lf\n",f[1]);
return 0;
}
T3 技术情报局
暴力考利枚举每个值,单调栈算出它成为最大值的区间,然后可以线段树求区间的子区间乘积和,具体维护区间元素乘积,前缀乘积和,后缀成积和与子区间乘积和,转移略。
正解在笛卡尔树上合并信息,可以去\(log\)。也可以在单调栈时直接合并,避免爆栈。
\(code:\)
T3
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL 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;
}
void write(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(signed i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=10000010;
int n,L,R,ans,mod;
unsigned s;
vector<int>v;
namespace GenHelper{
unsigned z1, z2, z3, z4, b;
unsigned rand_(){
b=((z1<<6)^z1)>>13;
z1=((z1&4294967294U)<<18)^b;
b=((z2<<2)^z2)>>27;
z2=((z2&4294967288U)<<2)^b;
b=((z3<<13)^z3)>>21;
z3=((z3&4294967280U)<<7)^b;
b=((z4<<3)^z4)>>12;
z4=((z4&4294967168U)<<13)^b;
return (z1^z2^z3^z4);
}
vector<int>get(int n,unsigned s,int l,int r){
vector<int>a; a.push_back(0);
z1=s;
z2=unsigned((~s)^0x233333333U);
z3=unsigned(s^0x1234598766U);
z4=(~s)+51;
for(signed i=1;i<=n;i++){
int x=rand_()&32767;
int y=rand_()&32767;
a.push_back(l+(x*32768+y)%(r-l+1));
}
return a;
}
}
namespace Cartesion_Tree{
int top,root,ls[NN],rs[NN],stk[NN];
struct node{
int sum,lll,rrr,mul;
node(){}
node(int a,int b,int c,int d){
sum=a; lll=b; rrr=c; mul=d;
}
node operator+(const node& rr)const{
node res;
res.sum=(sum+rr.sum+rrr*rr.lll)%mod;
res.lll=(lll+mul*rr.lll)%mod;
res.rrr=(rr.rrr+rrr*rr.mul)%mod;
res.mul=mul*rr.mul%mod;
return res;
}
}f[NN<<2];
void build(){
for(int k,i=1;i<=n;i++){
k=top;
while(k&&v[stk[k]]<v[i]) --k;
if(!k) root=i;
else rs[stk[k]]=i;
if(k<top) ls[i]=stk[k+1];
stk[++(top=k)]=i;
}
}
void dfs(int u){
f[u]=node(v[u],v[u],v[u],v[u]);
if(ls[u]) dfs(ls[u]), f[u]=f[ls[u]]+f[u];
if(rs[u]) dfs(rs[u]), f[u]=f[u]+f[rs[u]];
(ans+=v[u]*(mod+mod+f[u].sum-f[ls[u]].sum-f[rs[u]].sum))%=mod;
}
} using namespace Cartesion_Tree;
signed main(){
freopen("tio.in","r",stdin);
freopen("tio.out","w",stdout);
n=read(); s=read(); L=read(); R=read(); mod=read();
v=GenHelper::get(n,s,L,R);
build(); dfs(root);
write(ans,'\n');
return 0;
}
T4 肯德基
就是要求没有平方因子数的和。(也许是套路?
枚举平方因子,根据它的质因子个数进行容斥,发现容斥系数就是莫比乌斯函数。易得
\[ans=\sum_{i=1}^{\sqrt n}\mu(i)i^2\sum_{j=1}^{\left\lfloor \frac{n}{i^2}\right\rfloor}j \]\(i=1\)时为总方案,之后按质因子个数进行容斥。
对\(\left\lfloor\frac{n}{i^2}\right\rfloor\)整除分块,每次\(r^2\)为\(\left\lfloor \frac{n}{\left\lfloor n/l^2\right\rfloor}\right\rfloor\),因此\(r\)应取\(\sqrt{\left\lfloor \frac{n}{\left\lfloor n/l^2\right\rfloor}\right\rfloor}\),可以证明单次询问复杂度是\(O(\sqrt[3]{n})\)的(然而我不会
\(code:\)
T4
#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
namespace IO{
typedef long long LL;
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;
}
void write(int x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=10000010;
int t,n,mu[NN],pre[NN];
signed cnt,pri[NN];
bool vis[NN];
int S(int x){ return (x&1)?(x+1)/2*x:x/2*(x+1); }
void prprprprprprprpr(){
mu[1]=1;
for(int i=2;i<=1e7;i++){
if(!vis[i]) pri[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&pri[j]*i<=1e7;j++){
vis[pri[j]*i]=1;
if(i%pri[j]==0){ mu[pri[j]*i]=0; break; }
mu[pri[j]*i]=-mu[i];
}
}
for(int i=1;i<=1e7;i++)
pre[i]=pre[i-1]+mu[i]*i*i;
}
int ans(){
int l=1,r,ext=sqrt(n),res=0;
while(l<=ext){
r=sqrt(n/(n/l/l));
res+=S(n/l/l)*(pre[r]-pre[l-1]);
l=r+1;
}
return res;
}
signed main(){
freopen("kfc.in","r",stdin);
freopen("kfc.out","w",stdout);
t=read();
prprprprprprprpr();
while(t--){
n=read();
write(ans(),'\n');
}
return 0;
}