【比赛报告】CYJian的水题大赛[第三弹] NOIP练习赛卷七

比赛链接
搭配原题解食用更佳

A.食堂

题目链接
在这里插入图片描述
在这里插入图片描述


赛后题解把我秀到了,中间这段变形我是怎么也想不到的。
在这里插入图片描述
在这里插入图片描述

总结

当时做这道题写了两小时直接炸飞。首先就是这个取小数部分处理不来,然后就肯定GG了。
从这题能学到的:取小数部分的处理、公式变形(重点)、多次前缀和求解。


B.乘积

题目链接
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


赛后题解
利用斐波拉契数列相减找到位移量再线段树维护区间最小值,秀的飞起。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lc (o<<1)
#define rc (o<<1|1)
const int N=5e6+10,T=1e6+10,K=1e4+10;
int t,prime[K*40],p,fib[50],g[K],ans[K],mn[K<<5],lazy[K<<5],pos[K<<5],Id[K<<5],res[T];
bool iscomp[N];
short cnt[N];
struct node{
	int n,k,id;
	bool operator <(const node&rhs)const{
	return n<rhs.n;}
}ask[T];
void primetable()
{
	for(int i=2;i<N;i++)
	{
		if(!iscomp[i])prime[p++]=i,cnt[i]=1;
		for(int j=0;j<p&&i*prime[j]<N;j++)
		{
			iscomp[i*prime[j]]=1;cnt[i*prime[j]]=cnt[i]+1;
			if(i%prime[j]==0)break;
		}
	}
}
void build(int o,int l,int r)
{
	mn[o]=1;
	if(l==r)
	{
		Id[o]=l;pos[o]=o;
		return;
	}
	int mid=l+r>>1;
	build(lc,l,mid);build(rc,mid+1,r);
	pos[o]=pos[lc];
}
void push_down(int o,int l,int r)
{
	if(!lazy[o])return;
	int mid=l+r>>1;
	lazy[lc]+=lazy[o];lazy[rc]+=lazy[o];
	mn[lc]+=lazy[o];mn[rc]+=lazy[o];
	lazy[o]=0;
}
void update(int o,int l,int r,int nl,int nr,int v)
{
	if(nl<=l&&r<=nr)
	{
		mn[o]+=v;lazy[o]+=v;
		return;
	}
	push_down(o,l,r);
	int mid=l+r>>1;
	if(nl<=mid)update(lc,l,mid,nl,nr,v);
	if(nr>mid)update(rc,mid+1,r,nl,nr,v);
	mn[o]=min(mn[lc],mn[rc]);
	pos[o]=(mn[o]==mn[lc]?pos[lc]:pos[rc]);
}
int main()
{
	//freopen("in.txt","r",stdin);
	primetable();fib[0]=1,fib[1]=1;
	for(int i=2;i<50;i++)fib[i]=fib[i-1]+fib[i-2];
	for(int i=49;i>1;i--)fib[i]-=fib[i-1];
	for(int i=1;i<=10000;i++)g[i]=1;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	    scanf("%d%d",&ask[i].n,&ask[i].k),ask[i].id=i;
    sort(ask+1,ask+t+1);int m=ask[t].n;
    memset(ans,-1,sizeof(ans));
    build(1,1,10000);
    for(int i=1,mi,add,ID,id=1;id<=t&&i<=m;i++)
    {
    	mi=pos[1],ID=Id[mi];
    	update(1,1,10000,ID,ID,0);
    	add=mn[mi];
    	update(1,1,10000,1,ID,-add);
    	if(add>1&&ID<10000)
    	    update(1,1,10000,ID+1,10000,1-add);
		update(1,1,10000,ID,ID,fib[++g[ID]]);
		while(id<=t&&ask[id].n<i+add-1)res[ask[id].id]=ans[ask[id].k],id++;
    	i+=add-1;if(id>t||i>m)break;
    	ans[ID]+=cnt[i]+(ans[ID]<0);
		while(id<=t&&ask[id].n<=i)res[ask[id].id]=ans[ask[id].k],id++;
	}
	for(int i=1;i<=t;i++)printf("%d\n",res[i]);
	return 0;
}

总结

二麻二麻。斐波拉契数列相减那段挺好,还有那个最小值大于1的trick


C.乘积

题目链接
在这里插入图片描述
在这里插入图片描述


赛后题解
讲道理,中间那些公式变形看明白了,线性筛那段给我搞蒙了……h是算啥的,为啥要%(mod-1) QAQ

#include<cstdio>
const int mod=19260817,N=1e6+10;
int t,a,b,prime[N/10],p,d[N],h[N],f[N];
bool iscomp[N];
long long qpow(long long a,int b)
{
	long long ret=1;
	for(;b;b>>=1)
	{
		if(b&1)ret=ret*a%mod;
		a=a*a%mod;
	}
	return ret;
}
void primetable()
{
	for(int i=2;i<N;i++)
	{
		if(!iscomp[i])prime[p++]=i,d[i]=2,h[i]=i;
	    for(int j=0;j<p&&i*prime[j]<N;j++)
	    {
	    	iscomp[i*prime[j]]=1;
	    	if(i%prime[j]==0)
	    	{
	    		int tmp=i,cnt=2,F=1;
	    		while(tmp%prime[j]==0)tmp/=prime[j],F+=cnt,cnt++;
			    d[i*prime[j]]=d[tmp]*cnt;
				h[i*prime[j]]=1ll*qpow(h[tmp],d[i/tmp*prime[j]])*qpow(prime[j],1ll*F*d[tmp]%(mod-1))%mod;
				break;
			}
			d[i*prime[j]]=d[i]*2;
			h[i*prime[j]]=1ll*h[i]*h[i]%mod*qpow(prime[j],d[i])%mod;
		}
	}
}
void Init()
{
	f[0]=f[1]=h[0]=h[1]=d[1]=1;primetable();
	for(int i=2;i<N;i++)
    {
    	d[i]+=d[i-1];
    	f[i]=1ll*f[i-1]*qpow(i,d[i]%(mod-1))%mod;
    	h[i]=1ll*h[i-1]*h[i]%mod;
	}
	for(int i=2;i<N;i++)
	    h[i]=1ll*h[i-1]*h[i]%mod;
}
int main()
{
    Init();
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		scanf("%d%d",&a,&b);
		printf("%d\n",1ll*qpow(f[a-1],mod-2)*f[b]%mod*qpow(h[b],mod-2)%mod*h[a-1]%mod);
	}
	return 0;
}

赛后总结

生平最怕的数论专场……三场都看不懂(逃
学到的东西,最重要就是公式变形。把不好搞的部分拆成一些好搞的来搞也是种很好的trick。数据结构维护啥的也挺好。反正都挺好(我都做不起

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值