ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NOIP模拟87(多校20)

2021-11-01 16:05:46  阅读:139  来源: 互联网

标签:ch 20 NOIP int 多校 long times code define


前言

题目不难,但是个人感觉小细节有一些,然后有亿点卡常。。

感觉对于笛卡尔树的题目看不出算法,然后代码实现方面细节注意太少,常数有点大。

下次注意吧。

T1 集合均值

解题思路

感觉应该是期望题里面比较水的一种。

看了看范围大概的复杂度是 \(n\times m\) 是没问题了,然后看暴力分数比较丰厚,应该也不是一道难题。

枚举每一步操作,计算当前取出来的数字的期望值,然后同时算出来剩下数字的期望总和。

计算的时候除去当前数字的个数,也就是乘一个逆元,直接线性推就行了。

code

#include<bits/stdc++.h>
using namespace std;
inline int read()	
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){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 N=1e5+10,M=2e7+10,mod=998244353;
int n,m,ans,base,sum,inv[M];
int main()
{
	freopen("mos.in","r",stdin); freopen("mos.out","w",stdout);
	n=read(); m=read(); inv[1]=1; for(int i=2;i<=n*m+1;i++) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
	for(int i=1,x;i<=n;i++) x=read(),sum=(sum+1ll*x*m%mod)%mod;
	for(int i=1,temp;i<=n*m;i++) temp=1ll*sum*inv[n*m-i+1]%mod,base=(base+temp)%mod,ans=(ans+1ll*base*inv[i+1])%mod,sum=(sum-temp+mod)%mod;
	printf("%d",ans);
	return 0;
}

T2 聚烷撑乙二醇

解题思路

也是期望,但是相较于上一个题目有一小点的难度,边界需要注意,还有就是精度问题了。

发现正着去推有一点困难,于是我们选择反推,这样我们就可以得出来每一步的期望值了。

假设当前的区间是 \(L_i,R_i\) 下几个区间的最优策略期望值是 \(p\) 。

那么显然如果随机出来的数字在 \([L_i,p]\) 这个区间中的话我们一定是会选择尝试下一个的,毕竟风险比较低。

同样的道理如果随机出来的数字在 \((p,R_i]\) 这个区间中,我们是不会尝试下一个的,因为有风险。

于是这个区间的期望值就是:

\[\displaystyle p_i=\dfrac{p-L_i}{R_i-L_i}\times p+(1-\dfrac{p-L_i}{R_i-L_i})\times\dfrac{R_i+p}{2} \]

从最后一位直接推回去就好了。

code

#include<bits/stdc++.h>
#define int long long
#define double long double
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){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 N=1e6+10;
int n;
double ans,l[N],r[N];
#undef int
int main()
{
	#define int long long
	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();
	ans=(l[n]+r[n])/2.0;
	for(int i=n-1;i>=1;i--)
	{
		if(l[i]>=ans){ans=(l[i]+r[i])/2.0;continue;}
		if(r[i]<=ans||l[i]==r[i]) continue;
		ans=(ans-l[i])/(r[i]-l[i])*ans+(1-(ans-l[i])/(r[i]-l[i]))*(r[i]+ans)/2.0;
	}
	printf("%.5Lf",ans);
	return 0;
}

T3 技术情报局

解题思路

真的没看出来是笛卡尔树,但是貌似如果模数是质数的话可以直接 单调栈+前缀和 过掉。

然后对于这个题,单调栈+前缀和 加上暴扫单调栈就可以获得 75pts 的高分 code

发现对于最大值的维护可以直接建立一个大根笛卡尔树,然后每次将子树的信息合并上来就好了。

大概做法就是维护一个随着坐标的增加升序或者降序的和。

假设当前最大值是 \(maxn\) 。

不算中间值的左边的和 \(suf=L_1+L_1\times L_2+L_1\times L_2\times L_3···\)

不算中间值的右边的和 \(pre=R_1+R_1\times R_2+R_1\times R_2\times R_3···\)

于是当前的点的贡献就是 \((suf+1)\times(pre+1)\times maxn^2\)

对于信息的合并再维护一个子树内数字的乘积就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
#define ls son[x][0]
#define rs son[x][1]
using namespace std;
inline int read()	
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
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);
	}
}
const int N=1e7+10;
int n,ans,top,root,mod,sta[N],s[N],son[N][2],pre[N],suf[N],bas[N];
void init()
{
	using namespace GenHelper;
	int L,R,cnt=0; unsigned S;
	n=read(); S=read(); L=read(); R=read();
	z1 = S;
	z2 = unsigned((~S) ^ 0x233333333U);
	z3 = unsigned(S ^ 0x1234598766U);
	z4 = (~S) + 51;
	for (int i = 1; i <= n; i++)
	{
		int x = rand_() & 32767;
		int y = rand_() & 32767;
		s[++cnt]=L + (x * 32768 + y) % (R - L + 1);
	}
}
void dfs(int x)
{
	bas[x]=s[x];
	if(ls) dfs(ls),bas[x]=bas[x]*bas[ls]%mod;
	if(rs) dfs(rs),bas[x]=bas[x]*bas[rs]%mod;
	ans=(ans+(suf[ls]+1)*(pre[rs]+1)%mod*s[x]%mod*s[x])%mod;
	pre[x]=(pre[ls]+bas[ls]*s[x]%mod*(pre[rs]+1)%mod)%mod;
	suf[x]=(suf[rs]+bas[rs]*s[x]%mod*(suf[ls]+1)%mod)%mod;
}
#undef int
int main()
{
	#define int long long
	freopen("tio.in","r",stdin); freopen("tio.out","w",stdout);
	init(); mod=read();
	for(int i=1;i<=n;i++)
	{
		while(top&&s[sta[top]]<s[i]) son[i][0]=sta[top--];
		if(top) son[sta[top]][1]=i; sta[++top]=i;
	}
	while(top) root=sta[top--]; bas[0]=1; dfs(root);
	printf("%lld",ans);
	return 0;
}

T4 肯德基

解题思路

看到这个题目很难不想起 zxb 之前讲的 记忆这道题。

对于 80pts 做法可以容斥着去求,容斥系数是 \(\mu(i)\) 。

\[\displaystyle ans=\sum \mu(i)\times i^2\times \dfrac{\lfloor\frac{n}{i^2}\rfloor\times(\lfloor\frac{n}{i^2}\rfloor+1)}{2} \]

考虑优化,发现最后面的那一项可以整除分块,同时对于 \(\mu(i)\times i^2\) 做一个前缀和就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()	
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){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 N=1e7+10;
int T,cnt,pri[N],mu[N];
ull ans,n,pre[N];
bool vis[N];
inline void pre_work(int lim)
{
	mu[1]=1;
	for(int i=2;i<=lim;i++)
	{
		if(!vis[i]) pri[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&pri[j]*i<=lim;j++)
		{
			vis[pri[j]*i]=true;
			if(i%pri[j]) mu[pri[j]*i]=-mu[i];
			else break;
		}
	}
	for(int i=1;i<=lim;i++) pre[i]=pre[i-1]+mu[i]*i*i;
}
inline ull mul(ull x){if(x&1) return (x+1)/2*x;return x/2*(x+1);}
inline void solve()
{
	n=read(); ans=mul(n);
	for(ull l=2,r;l*l<=n;l=r+1)
	{
		r=sqrt(n/(n/(l*l)));
		ull temp=n/(l*l),num=mul(temp);
		ans+=(pre[r]-pre[l-1])*num;
	}
	printf("%llu\n",ans);
}
#undef int
int main()
{
	#define int long long
	freopen("kfc.in","r",stdin); freopen("kfc.out","w",stdout);
	pre_work(10000000); T=read(); while(T--) solve(); return 0;
}

标签:ch,20,NOIP,int,多校,long,times,code,define
来源: https://www.cnblogs.com/Varuxn/p/15494117.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有