T1集合均值 T2聚烷撑乙二醇 T3技术情报局 T4肯德基

今天好像全是简单题,但仍然没想到多少正解,考完一个半小时就改完了。。

对各种知识点还是没有理解透,一些套路想不出来

\(\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})\)的(然而我不会

另外,将\(\mu^2(n)=\sum_{d^2|n}\mu(d)\)代入也可以直接得到上面的式子。

\(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;
}