JXOI2018题解

「JXOI2018」游戏

显然只要检查完所有除自己外没有范围 [ l , r ] [l,r] [l,r]的因数的数就可以使所有办公室开始认真工作,那么只需要求出这种数有多少,后面就随便算了。那么只需要知道最大因数是否在 [ l , r ] [l,r] [l,r]内,线性筛出每个数的最小质因数就可以知道最大因数了。

Code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int inf=2147483647;
const int mod=1000000007;
const int Maxn=10000010;
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<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
void upd(int&x,int y){x+=y;if(x>=mod)x-=mod;}
int Pow(int x,int y)
{
	if(!y)return 1;
	int t=Pow(x,y>>1),re=(LL)t*t%mod;
	if(y&1)re=(LL)re*x%mod;
	return re;
}
int n,m=0,l,r,f[Maxn],prime[1000000],tot=0,inv[Maxn];
bool mark[Maxn];
void pre()
{
	inv[0]=inv[1]=1;for(int i=2;i<=r;i++)inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod;
	f[1]=2;
	for(int i=2;i<=r;i++)
	{
		if(!mark[i])f[i]=i,prime[++tot]=i;
		for(int j=1;j<=tot&&prime[j]*i<=r;j++)
		{
			mark[prime[j]*i]=true;
			f[prime[j]*i]=prime[j];
			if(i%prime[j]==0)break;
		}
	}
}
int main()
{
	l=read(),r=read();n=r-l+1;pre();
	for(int i=l;i<=r;i++)if(i/f[i]<l)m++;
	int ans=0,t=1,C=1;
	for(int i=2;i<=n-m;i++)t=(LL)t*i%mod;
	for(int i=2;i<=m;i++)t=(LL)t*i%mod;
	for(int p=m;p<=n;p++)
	{
		upd(ans,(LL)C*t%mod*p%mod);
		C=(LL)C*p%mod*inv[p-m+1]%mod;
	} 
	printf("%d",ans);
}

「JXOI2018」排序问题

稍微推一下式子就发现每次加入的数就是当前最少的那个,然后模拟这个过程就可以了。

Code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=200010;
const int Maxm=10000010;
const int inf=2147483647;
const int mod=998244353;
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<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
int n,m,l,r,a[Maxn],b[Maxn],ans,inv[Maxm],fin[Maxm],cnt[Maxn],fac[Maxm];
unordered_map<int,int>c;
int Pow(int x,int y)
{
	if(!y)return 1;
	int t=Pow(x,y>>1),re=(LL)t*t%mod;
	if(y&1)re=(LL)re*x%mod;
	return re;
}
int main()
{
	fac[0]=1;for(int i=1;i<=10000000;i++)fac[i]=(LL)fac[i-1]*i%mod;
	inv[0]=inv[1]=1;for(int i=2;i<=10000000;i++)inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod;
	fin[0]=1;for(int i=1;i<=10000000;i++)fin[i]=(LL)fin[i-1]*inv[i]%mod;
	int T=read();
	while(T--)
	{
		int N;
		n=read(),m=read(),l=read(),r=read();N=n;
		ans=fac[m];
		for(int i=1;i<=n;i++)a[i]=b[i]=read(),c[a[i]]++,ans=(LL)ans*(i+m)%mod;
		sort(a+1,a+1+n);n=unique(a+1,a+1+n)-(a+1);
		int L=n+1,R=0;
		for(int i=1;i<=n;i++)
		if(a[i]<l)ans=(LL)ans*fin[c[a[i]]]%mod;
		else{L=i;break;}
		for(int i=n;i;i--)
		if(a[i]>r)ans=(LL)ans*fin[c[a[i]]]%mod;
		else{R=i;break;}
		for(int i=0;i<=N;i++)cnt[i]=0;
		cnt[0]=r-l+1-(R-L+1);
		for(int i=L;i<=R;i++)cnt[c[a[i]]]++;
		for(int i=0;i<N;i++)
		{
			if(cnt[i]<=m)
			{
				cnt[i+1]+=cnt[i];
				m-=cnt[i];cnt[i]=0;
			}
			else
			{
				cnt[i+1]+=m;
				cnt[i]-=m;m=0;break;
			}
		}
		if(!m)
		{
			for(int i=2;i<=N;i++)ans=(LL)ans*Pow(fin[i],cnt[i])%mod;
		}
		else
		{
			int p=N+m/cnt[N],v2=m%cnt[N],v1=cnt[N]-v2;
			ans=(LL)ans*Pow(fin[p],v1)%mod;
			ans=(LL)ans*Pow(fin[p+1],v2)%mod;
		}
		printf("%d\n",ans);
		for(int i=1;i<=N;i++)c[b[i]]--;
	} 
}

「JXOI2018」守卫

枚举右端点 r r r,显然 r r r必须要放,那么 r r r看不到的一定是若干个区间,设他们为 [ l i , r i ] [l_i,r_i] [li,ri],那么若要覆盖这些,最后一个要么放在 r i r_i ri,要么放在 r i + 1 r_i+1 ri+1,因为 r i + 2 r_i+2 ri+2以及之后是看不到 r i r_i ri的。那么就好办了,直接 f l , r f_{l,r} fl,r表示这段最少放多少就可以了。

Code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=5010;
const int inf=2147483647;
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<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
int n,f[Maxn][Maxn],tmp[Maxn],lt;LL h[Maxn];
int main()
{
	n=read();
	for(int i=1;i<=n;i++)h[i]=read();
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		f[i][i]=f[i-1][i]=1;
		LL y=h[i]-h[i-1],x=1;
		bool fir=true;int r;
		for(int j=i-2;j>0;j--)
		{
			LL Y=h[i]-h[j],X=i-j;
			if(Y*x<X*y)f[j][i]=f[j+1][i],fir=true,x=X,y=Y;
			else
			{
				if(fir)f[j][i]=f[j+1][i]+1,fir=false,r=j;
				else f[j][i]=min(f[j][r]+f[r+1][i],f[j][r+1]+f[r+2][i]);
			}
		}
	}
	for(int l=1;l<=n;l++)
	for(int r=l;r<=n;r++)
	ans^=f[l][r];
	printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值