8.24考试总结(NOIP模拟47)[Prime·Sequence·Omeed]

时间带着明显的恶意,缓缓在我的头顶流逝。

T1 Prime

解题思路

成功没有签上到。。。

一看数据范围 \(R-L+1\le 10^7,R\le 10^{14}\) ,这肯定是判断范围内的数字是否可行之后直接暴力扫啊。。

然后我们就考虑线性筛,筛出 \(\min(\sqrt{R},K)\) 之间的素数。

算出素数来有什么用呢(没什么用,所以我们筛完之后不用)

发现对于一个数字 \(x\) 而言,它除了本身之外的因数一定是 \(\le \sqrt{x}\) 的。

然后我们就可以根据已经筛出来的素数直接暴力向后更新倍数就好了,设素数个数是 \(n\) ,调和级数所以复杂度是 \(n\times ln\;n\) 的。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<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 l,r,m,ans,cnt,pri[N];
bool vis[N],b[N];
void Prime()
{
	int temp=min((int)sqrt(r),m);
	for(int i=2;i<=temp;i++)
	{
		if(!vis[i])	pri[++cnt]=i;
		for(int j=1;j<=cnt&&pri[j]*i<=temp;j++)
		{
			vis[i*pri[j]]=true;
			if(i%pri[j]==0)	break;
		}
	}
}
signed main()
{
	l=read();	r=read();	m=read();
	Prime();
	int temp=min((int)sqrt(r),m);
	for(int i=1;i<=cnt;i++)
		for(int j=ceil((1.0*l)/(1.0*pri[i]));j<=r/pri[i];j++)
			b[j*pri[i]-l]=true;
	for(int i=l;i<=r;i++)
	{
		if(i<=temp){if(vis[i])	continue;}
		else	if(b[i-l])	continue;
		ans^=i;
	}
	printf("%lld",ans);
	return 0;
}

T2 Sequence

解题思路

矩阵乘法优化 DP。

先考虑怎么算不同子序列个数。设 \(f_i\) 表示当前以i结尾的子序列个数,如果整个序列的

下一个元素是 \(x\), 那么令 \(f_x=1+\sum\limits_{i=1}^k f_i\),其它的dp值保持不变。

发现无论下一个数字填什么其实都是一样的因此我们要最大化 \(\sum\limits_{i=1}^k f_i\),贪心选择剩下 DP 值最小的,也就是最后出现位置最靠前的位置。

然后对于这样的转移我们可以选择矩阵乘法,一次搞 k 次可以通过此题。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<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,M=110,mod=1e9+7;
const double base=1e7;
int n,m,K,f[M],s[N],id[N],e[M],fro[N];
struct Matrix
{
	int s[M][M];
	Matrix(){memset(s,0,sizeof(s));}
	Matrix friend operator * (Matrix x,Matrix y)
	{
		Matrix sum;
		for(int i=1;i<=K+1;i++)
			for(int j=1;j<=K+1;j++)
			{
				for(int k=1;k<=K+1;k++)
					sum.s[i][j]=(sum.s[i][j]+x.s[i][k]*y.s[k][j]%mod);
				sum.s[i][j]%=mod;
			}
		return sum;
	}
}ans,mat;
Matrix power(Matrix x,int y)
{
	Matrix sum;
	for(int i=1;i<=K+1;i++)
		sum.s[i][i]=1;
	while(y)
	{
		if(y&1)	sum=sum*x;
		x=x*x;
		y>>=1;
	}
	return sum;
}
bool comp(int x,int y)
{
	return fro[x]<fro[y];
}
signed main()
{
	n=read();	m=read();	K=read();
	int sum=0;
	for(int i=1,las;i<=n;i++)
	{
		s[i]=read();	las=sum;
		sum=(2*sum+1-f[s[i]]+mod)%mod;
		f[s[i]]=las+1;	fro[s[i]]=i;
	}
	for(int i=1;i<=K;i++)	id[i]=i;
	sort(id+1,id+K+1,comp);
	for(int i=1;i<=K;i++)	ans.s[1][i]=f[id[i]];
	ans.s[1][K+1]=1;
	for(int i=1;i<=K;i++)
	{
		memset(e,0,sizeof(e));
		e[i]=1;	sum=1;
		for(int j=1;j<=K;j++)
		{
			int temp=sum;
			sum=(sum*2-e[j]+mod)%mod;
			e[j]=temp;
		}
		for(int j=1;j<=K;j++)
			mat.s[i][j]=e[j];
	}
	memset(e,0,sizeof(e));
	sum=0;
	for(int i=1;i<=K;i++)
	{
		int temp=sum;
		sum=(sum*2+1-e[i]+mod)%mod;
		e[i]=temp+1;
	}
	for(int i=1;i<=K;i++)
		mat.s[K+1][i]=e[i];
	mat.s[K+1][K+1]=1;	sum=0;
	ans=ans*power(mat,m/K);
	for(int i=1;i<=K;i++)
		f[i]=ans.s[1][i],sum=(sum+f[i])%mod;
	for(int i=1;i<=m%K;i++)
	{
		int temp=sum;
		sum=(sum*2+1-f[i]+mod)%mod;
		f[i]=temp+1;
	}
	printf("%lld",sum);
	return 0;
}

T3 Omeed

解题思路

大概有两种打法吧。。

对于基础分数的贡献十分简单在此不做过多的赘述。

对于连击分数的每个位置 \(i\) 对于下一位的贡献设为 \(f_i\),就有了下面的柿子:

\[f_i=p_i\times (f_{i-1}+1)+(1-p_i)\times f_{i-1}\times t \]

经过层层化简就可以得到类似于 \(f_{i}=k\times f_{L-1}+b\) 的柿子,然后发现 \(k\times f_{L-1}\) 啥用没有,于是可以直接维护 \(b\) 的加和。

上面的是一种做法,但是实现起来比较麻烦,我直接。。。

下面讲一下另一种做法。。。

还是化简柿子,可以得到 \(\displaystyle f_k=\sum_{i=L}^{k}p_i\prod_{j=i+1}^{k}(t+p_j-p_j\times t)\)

然后对于每一个区间也是维护 \(k,b\) ,与上面做法不同的是这里的取值与左端点 \(f_L\) 的取值有关系,因此需要对于每个区间的右端点进行维护,用于合并信息。

亿点卡常。

code

#include<bits/stdc++.h>
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls x<<1
#define rs x<<1|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;
}
const int N=5e5+10,mod=998244353;
int task,ta,tb,n,q,t,a,b,p[N],f[N];
int power(int x,int y)
{
	int temp=1;
	while(y)
	{
		if(y&1)	temp=1ll*temp*x%mod;
		x=1ll*x*x%mod;
		y>>=1;
	}
	return temp;
}
int inv(int x){return power(x,mod-2)%mod;}
struct Segment_Tree
{
	int dat,k,b,datk,datb;
}tre[N<<1];
void update(int x,int num)
{
	tre[x].dat=tre[x].b=tre[x].datk=tre[x].datb=num;
	tre[x].k=(t+num-1ll*num*t%mod+mod)%mod;
}
void push_up(int x)
{
	tre[x].dat=(tre[ls].dat+tre[rs].dat)%mod;
	tre[x].k=1ll*tre[ls].k*tre[rs].k%mod;
	tre[x].b=(1ll*tre[rs].k*tre[ls].b+tre[rs].b)%mod;
	tre[x].datk=(tre[ls].datk+1ll*tre[rs].datk*tre[ls].k)%mod;
	tre[x].datb=(tre[ls].datb+1ll*tre[rs].datk*tre[ls].b+tre[rs].datb)%mod;
}
void insert(int x,int l,int r,int pos,int num)
{
	if(l==r)	return update(x,num),void();
	int mid=(l+r)>>1;
	if(pos<=mid)	insert(ls,l,mid,pos,num);
	else	insert(rs,mid+1,r,pos,num);
	push_up(x);
}
Segment_Tree query_seg(int x,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)	return tre[x];
	int mid=(l+r)>>1;
	Segment_Tree ans1=(Segment_Tree){-1,0,0,0,0};
	Segment_Tree ans2=(Segment_Tree){-1,0,0,0,0};
	if(L<=mid)	ans1=query_seg(ls,l,mid,L,R);
	if(R>mid)	ans2=query_seg(rs,mid+1,r,L,R);
	if(~ans1.dat&&~ans2.dat)
		return (Segment_Tree){(ans1.dat+ans2.dat)%mod,1ll*ans1.k*ans2.k%mod,(1ll*ans2.k*ans1.b+ans2.b)%mod,(ans1.datk+1ll*ans2.datk*ans1.k)%mod,(ans1.datb+1ll*ans2.datk*ans1.b+ans2.datb)%mod};
	if(~ans1.dat)	return ans1;
	return ans2;
}
signed main()
{
	task=read();
	n=read();	q=read();
	ta=read();	tb=read();
	t=1ll*ta*inv(tb)%mod;
	a=read();	b=read();
	for(int i=1,pa,pb;i<=n;i++)
	{
		pa=read();	pb=read();
		p[i]=1ll*pa*inv(pb)%mod;
		insert(1,1,n,i,p[i]);
	}
	while(q--)
	{
		int opt,pos,pa,pb,l,r;
		opt=read();
		if(!opt)
		{
			pos=read();	pa=read();	pb=read();
			p[pos]=1ll*pa*inv(pb)%mod;
			insert(1,1,n,pos,p[pos]);
			continue;
		}
		l=read();	r=read();
		if(l==r){printf("%lld\n",1ll*(a+b)*p[l]%mod);continue;}
		Segment_Tree temp=query_seg(1,1,n,l+1,r);
		printf("%lld\n",(1ll*a*(1ll*temp.dat+p[l])%mod+1ll*(1ll*p[l]*temp.datk%mod+1ll*temp.datb+p[l])*b%mod)%mod);
	}
	return 0;
}
posted @ 2021-08-25 08:35  Varuxn  阅读(67)  评论(0编辑  收藏  举报