20240405比赛总结

寄的很惨

T1 [JLOI2014] 聪明的燕姿

https://gxyzoj.com/d/hzoj/p/3672

敲个警钟,千万不要用一些奇怪的方法写自己会的题,不然大概率会一分不剩

由小学奥数知识,约数和的求法为(1+pi2+pi3++piai)

所以,可以先线性预处理出约数和,再直接统计,时间复杂度O(nk),显然会T

考虑优化,显然,约数和也是一个乘积所以可以枚举其因子,注意,同一个p只可以使用一次

所以可以dfs,记录当前的答案和剩余的数,但是,2e9以内的质数不会少于5e7个,所以,还要优化

因为一个数n中不会出现两个超过n的因数,所以,考虑只枚举109以内的因数,其余暴力判断即可

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=2e9;
int s,idx,ans,t[100005];
bool get_prime(int x)
{
	if(x==1||x==0) return 0;
	for(int i=2;i<=x/i;i++)
	{
		if(x%i==0) return 0;
	}
	return 1;
}
int cnt,p[50005];
bool vis[50005];
void dfs(int x,int num,int id)
{
	//printf("%d %d %d\n",x,num,id);
	if(x==1)
	{
		t[++ans]=num;
		return;
	}
	for(int i=id;i<=cnt&&p[i]*p[i]<=x;i++)
	{
		ll tmp=1,sum=1;
		for(int j=1;;j++)
		{
			tmp*=p[i];
			sum+=tmp;
			if(sum<=x)
			{
				if(x%sum==0)
				{
					dfs(x/sum,num*tmp,i+1);
				}
			}
			else break;
		}
	}
	if(get_prime(x-1)&&x-1&&x-1>=p[id]) t[++ans]=(x-1)*num;
}
int main()
{
	for(int i=2;i<=50000;i++)
	{
		if(!vis[i]) p[++cnt]=i;
		for(int j=1;i*p[j]<=50000;j++)
		{
			vis[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}
	while(cin>>s)
	{
		ans=0;
		dfs(s,1,1);
		sort(t+1,t+ans+1);
		int tmp=ans;
		for(int i=1;i<=ans;i++)
		{
			if(t[i]==t[i-1]) tmp--;
		}
		printf("%d\n",tmp);
		for(int i=1;i<=ans;i++)
		{
			if(t[i]!=t[i-1]) printf("%d ",t[i]);
		}
		if(ans)
		printf("\n");
	}
	return 0;
}


T2 luogu4550收集邮票

https://gxyzoj.com/d/hzoj/p/3671

fi表示目前取了i张不同的邮票,取到n张不同的邮票还需取的期望次数,gi则为期望价格

很明显fn=gn=0

先考虑fi,未取到新的邮票的概率为nin,取到的概率为in

所以,得:fi=fi×in+fi+1×nin+1

接着考虑g,有f的结论可以得到gi=(gi+fi+1)×in+(fi+1+gi+1+1)×nin

为什么式子这样写?因为越往后每次的花费越大,所以后面在多取一次后都要加1,而本身这多取的一次也要加1,所以可得上述式子

化简得:gi=nni+fi×ini+fi+1+gi+1

代码:

#include<cstdio>
using namespace std;
int n;
double f[10004],g[10004];
int main()
{
	scanf("%d",&n);
	for(int i=n-1;i>=0;i--)
	{
		f[i]=f[i+1]+1.0*n/(n-i);
		g[i]=1.0*n/(n-i)+f[i]*i/(n-i)+f[i+1]+g[i+1];
	}
	printf("%.2lf",g[0]);
	return 0;
}

T3 [HAOI2018] 苹果树

https://gxyzoj.com/d/hzoj/p/3665

最简单的想法,直接暴力枚举树的形态,再暴力计算两点之间的距离,时间复杂度O(n!)

转换思路,可以从边考虑,设深度较大的一点是i,则i子树内一点到i子树外一点的路径必然经过该边,方案数:sizei(nsizei)

因为总形态数为n!,所以只用考虑有多少种情况满足i的子树大小为sizei即可

本题n的范围是2000,所以,时间复杂度为O(n2)

考虑枚举节点i和其子树大小sizei

分别考虑i的子树内与子树外的方案数

首先,因为i的子树内的节点编号必然大于i,树的形态为i!,所以子树内的方案数为sizei!×Cnisizei1

再考虑子树外,在放i之前,方案数为i!,放i之后,剩余的点就不能放在i的子树内了,所以方案数为:(i+11)(i+21)(nsizei2+1)

所以子树外的总方案数为i(i1)(nsizei1)!

相乘后就是答案

代码:

#include<cstdio>
#define ll long long
using namespace std;
int n,p;
ll fac[2005],C[2005][2005],ans;
int main()
{
	scanf("%d%d",&n,&p);
	fac[0]=1;
	for(int i=1;i<=n;i++)
	{
		fac[i]=fac[i-1]*i%p;
	}
	C[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++)
		{
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n-i+1;j++)
		{
			ans+=j*(n-j)%p*fac[j]%p*C[n-i][j-1]%p*i%p*(i-1)%p*fac[n-j-1]%p;
			ans%=p;
		}
	}
	printf("%d",ans);
	return 0;
}

T4 [HAOI2012]高速公路

https://gxyzoj.com/d/hzoj/p/P691

考虑最朴素的式子ans=i=lri=lrdisi,jCrl+12,时间复杂度O(n2m)

可以将边转化为点,则每个点的经过次数乘该点的权值就是答案,同时r也要-1,所以式子是:ans=ilrai×(ri+1)(il+1)

时间复杂度O(nm)

考虑优化:看到数据范围和区间修改,很明显是线段树

将第个式子拆开,得到:ans=(rl+1rl)×i=lrai+(r+l)×(l+r)i=lrai×i+i=lrai×i2

sum1=i=lrai

sum2=i=lrai×i

sum3=i=lrai×i2

在修改时,sum1可以直接进行更改,而sum2和sum3则需要记录两个值sum4和sum5

其中sum4=i

sum5=i2

这两个值可以在预处理时直接计算,所以,时间复杂度O(lognm)

记得开long long

代码:

#include<cstdio>
#include<iostream>
#define ll long long
#define lid id<<1
#define rid id<<1|1
using namespace std;
int n,m;
struct seg_tree{
	int l,r;
	ll sum[6],lazy;
}tr[800040];
void build(int id,int l,int r)
{
	tr[id].l=l,tr[id].r=r;
	if(l==r)
	{
		tr[id].sum[4]=l;
		tr[id].sum[5]=1ll*l*l;
		return;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	tr[id].sum[4]=tr[lid].sum[4]+tr[rid].sum[4];
	tr[id].sum[5]=tr[lid].sum[5]+tr[rid].sum[5];
}
void pushdown(int id)
{
	if(tr[id].lazy)
	{
		tr[lid].lazy+=tr[id].lazy;
		tr[rid].lazy+=tr[id].lazy;
		tr[lid].sum[1]+=tr[id].lazy*(tr[lid].r-tr[lid].l+1);
		tr[rid].sum[1]+=tr[id].lazy*(tr[rid].r-tr[rid].l+1);
		tr[lid].sum[2]+=tr[id].lazy*tr[lid].sum[4];
		tr[rid].sum[2]+=tr[id].lazy*tr[rid].sum[4];
		tr[lid].sum[3]+=tr[id].lazy*tr[lid].sum[5];
		tr[rid].sum[3]+=tr[id].lazy*tr[rid].sum[5];
		tr[id].lazy=0;
	}
}
void update(int id,int l,int r,ll c)
{
	if(tr[id].l==l&&tr[id].r==r)
	{
		tr[id].sum[1]+=c*(r-l+1);
		tr[id].lazy+=c;
		tr[id].sum[2]+=c*tr[id].sum[4];
		tr[id].sum[3]+=c*tr[id].sum[5];
		return;
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1;
	if(r<=mid) update(lid,l,r,c);
	else if(l>mid) update(rid,l,r,c);
	else update(lid,l,mid,c),update(rid,mid+1,r,c);
	tr[id].sum[1]=tr[lid].sum[1]+tr[rid].sum[1];
	tr[id].sum[2]=tr[lid].sum[2]+tr[rid].sum[2];
	tr[id].sum[3]=tr[lid].sum[3]+tr[rid].sum[3];
}
ll sum1,sum2,sum3;
void query(int id,int l,int r)
{
	if(l==tr[id].l&&tr[id].r==r)
	{
		sum1+=tr[id].sum[1];
		sum2+=tr[id].sum[2];
		sum3+=tr[id].sum[3];
		return;
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1;
	if(r<=mid) query(lid,l,r);
	else if(l>mid) query(rid,l,r);
	else query(lid,l,mid),query(rid,mid+1,r);
}
ll gcd(ll a,ll b)
{
	if(b==0) return a;
	return gcd(b,a%b);
}
int main()
{
	scanf("%d%d",&n,&m);
	build(1,1,n-1);
	while(m--)
	{
		char opt;
		cin>>opt;
		if(opt=='C')
		{
			int l,r;
			ll v;
			scanf("%d%d%lld",&l,&r,&v);
			update(1,l,r-1,v);
		}
		else
		{
			sum1=sum2=sum3=0;
			int l,r;
			scanf("%d%d",&l,&r);
			r--;
			query(1,l,r);
			ll a=sum1*(r-l+1-1ll*r*l)+(l+r)*sum2-sum3;
			ll b=1ll*(r+1-l)*(r-l+2)/2;
			ll d=gcd(a,b);
			printf("%lld/%lld\n",a/d,b/d);
		}
	}
	return 0;
}
posted @   wangsiqi2010916  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示