Codeforces 1114 简要题解

传送门
然而这场div2div2div2没有什么难度比较大的题

A题

传送门
题意简述:三个人分别至少选x,y,zx,y,zx,y,z件物品,有三种物品数量分别为a,b,ca,b,ca,b,c,其中第一个人只能选第一种,第二个人不能选第三种,第三个人随意问能否满足三个人需求。


思路:
直接模拟即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+5,mod=1e9+7;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int read(){
	int ans=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans*w;
}
int x,y,z,a,b,c;
int main(){
	x=read(),y=read(),z=read(),a=read(),b=read(),c=read();
	if(x>a)return puts("NO"),0;
	a-=x;
	a+=b;
	if(y>a)return puts("NO"),0;
	a-=y,a+=c;
	if(z>a)return puts("NO"),0;
	puts("YES");
	return 0;
}

B题

传送门
题意简述:
要求把nnn个数的数列分成kkk份,每份至少有mmm个数,问这kkk份的前mmm大数之和的和最大值是多少,并要求给出一种构造方案。


思路:
显然可以构造出一种方案满足所有组的前mmm大拼起来是整个数列的前k∗mk*mkm大,排序分组即可。

代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=2e5+5,mod=1e9+7;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int read(){
	int ans=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans*w;
}
int n,m,k;
struct Node{int val,id;}a[N];
inline bool cmp1(const Node&a,const Node&b){return a.val>b.val;}
inline bool cmp2(const Node&a,const Node&b){return a.id<b.id;}
int main(){
	n=read(),m=read(),k=read();
	for(ri i=1;i<=n;++i)a[i].val=read(),a[i].id=i;
	sort(a+1,a+n+1,cmp1);
	ll ans=0;
	for(ri i=1;i<=k*m;++i)ans+=a[i].val;
	cout<<ans<<'\n';
	sort(a+1,a+k*m+1,cmp2);
	for(ri i=m;i<k*m;i+=m)cout<<a[i].id<<' ';
	return 0;
}

C题

传送门
题意简述:给出 两个数n,b,n≤1e18,b≤1e12n,b,n\le1e18,b\le1e12n,b,n1e18,b1e12,问n!n!n!bbb进制下有多少个后缀000


思路:
b=a1k1a2k2...apkpb=a_1^{k_1}a_2^{k_2}...a_p^{k_p}b=a1k1a2k2...apkp
然后算算n!n!n!中分别有多少个a1,a2,...,apa_1,a_2,...,a_pa1,a2,...,ap,算出来之后除以kpk_pkp更新答案即可。
注意不要爆long long
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=2e5+5,mod=1e9+7;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline ll read(){
	ll ans=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans*w;
}
ll n,b;
inline ll calc(ll n,ll x,ll tim){
	ll res=0,ret=0;
	for(ll mul=x;mul<=n;mul*=x){
		ret+=n/mul;
		res+=ret/tim,ret%=tim;
		if(mul>n/x)break;
	}
	return res;
}
inline ll solve(ll n,ll p){
	ll res=2e18,up=p;
	for(ri i=2;(ll)i*i<=up;++i){
		if(p!=p/i*i)continue;
		ll cnt=0;
		while(p==p/i*i)p/=i,++cnt;
		res=min(res,calc(n,i,cnt));
	}
	if(p^1)res=min(res,calc(n,p,1));
	return res;
}
int main(){
	n=read(),b=read();
	cout<<solve(n,b);
	return 0;
}

D题

传送门
题意简述:给出一个nnn个数的数列(n≤5000)(n\le5000)(n5000),称一段相同的数为一个连通块,你每次可以选择一个连通块把它整体变成另一个数,问把整个数列变成同一个数的最少次数。


思路:先把连通块压成一个数,然后区间dpdpdp转移一下即可。

代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=5005,mod=1e9+7;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline ll read(){
	ll ans=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans*w;
}
inline void update(int&a,const int&b){a=b<a?b:a;}
int n,c[N],tot=0,f[N][N];
int main(){
	n=read();
	for(ri i=1;i<=n;++i)c[i]=read();
	for(ri i=1;i<=n;++i)if(c[i]^c[tot])c[++tot]=c[i];
	n=tot;
	memset(f,0x3f,sizeof(f));
	for(ri i=1;i<=n;++i)f[i][i]=0;
	for(ri len=2;len<=n;++len){
		for(ri l=1,r=l+len-1;r<=n;++l,++r){
			if(c[l]==c[r])update(f[l][r],f[l+1][r-1]+1);
			update(f[l][r],1+f[l+1][r]);
			update(f[l][r],f[l][r-1]+1);
		}
	}
	cout<<f[1][n];
	return 0;
}

E题

传送门
题意简述:交互题。
现在有一个nnn个数的n≤1e6n\le1e6n1e6的打乱顺序的等差数列,保证ai≤1e9,公差&gt;0a_i\le1e9,公差&gt;0ai1e9,>0,你可以问不超过606060次来找到这个数列的首项和公差。
你可以问的问题如下:

  • &gt; x&gt;\ x> x:询问数列是否存在一个数&gt;x&gt;x>x,存在返回111否则返回000
  • ? x?\ x? x:询问数列第xxx个数的值(打乱后的第xxx个)。

思路:先问第一种问题303030次找到数列的最大值,然后随机出一些位置求它们跟最大值差值的gcdgcdgcd作为公差。

代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,ll> pii;
const int N=4e5+5,mod=1e9+7;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,mx,d,mn;
map<int,int>mp;
int main(){
	srand(time(NULL));
	n=read();
	int l=0,r=1e9,mx=1e9;
	int tim=0;
	while(l<=r){
		int mid=l+r>>1;
		cout<<"> "<<mid<<endl<<endl;
		if(read())l=mid+1;
		else r=mid-1,mx=mid;
	}
	int d=0;
	for(ri x,i=1;i<=min(29,n);++i){
		while(1){
			x=(ll)rand()*rand()%n+1;
			if(!mp[x]){mp[x]=1;break;}
		}
		cout<<"? "<<x<<endl<<endl;
		x=read();
		d=__gcd(d,mx-x);
	}
	cout<<"! "<<mx-(n-1)*d<<' '<<d<<endl<<endl;
	return 0;
}

F题

传送门
题意简述:现在有一个长度为nnn的数列n≤4e5n\le4e5n4e5,数列里的数最开始≤300\le300300,现在要求支持如下操作:

  1. 区间乘一个数v≤300v\le300v300
  2. 求区间数的乘积的欧拉函数值。

询问数≤2e5\le2e52e5,答案对1e9+71e9+71e9+7取模。


思路:询问操作相当于问的是这个东西:Mull,r∗∏primei,primei is a factor of Mul[l,r]primei−1primeiMul_{l,r}*\prod_{prime_i,prime_i\ is\ a\ factor\ of\ Mul_{[l,r]}} \frac{prime_i-1}{prime_i}Mull,rprimei,primei is a factor of Mul[l,r]primeiprimei1
前面的积可以用线段树维护,考虑到300300300以内的素数只有626262个,我们把它们的存在性用一个longlonglong longlonglong压起来再用线段树维护即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,ll> pii;
const int N=4e5+5,mod=1e9+7;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int pri[305],invp[305],n,q,tot=-1;
bool vis[305];
inline void init(){
	for(ri i=2;i<=300;++i){
		if(!vis[i])pri[++tot]=i,invp[tot]=ksm(i,mod-2);
		for(ri j=0;j<=tot&&i*pri[j]<=300;++j){
			vis[i*pri[j]]=1;
			if(i==i/pri[j]*pri[j])break;
		}
	}
}
inline ll solve(int val){
	ll ret=0;
	for(ri i=0;i<=tot;++i)if(val==val/pri[i]*pri[i])ret|=1ll<<i;
	return ret;
}
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (l+r>>1)
	int val[N<<2],lz1[N<<2],len[N<<2];
	ll exi[N<<2],lz2[N<<2];
	inline void pushup(int p){val[p]=mul(val[lc],val[rc]),exi[p]=exi[lc]|exi[rc];}
	inline void pushnow(int p,int v1,ll v2){val[p]=mul(val[p],ksm(v1,len[p])),exi[p]|=v2,lz1[p]=mul(lz1[p],v1),lz2[p]|=v2;}
	inline void pushdown(int p){if(lz1[p]==1&&!lz2[p])return;pushnow(lc,lz1[p],lz2[p]),pushnow(rc,lz1[p],lz2[p]),lz1[p]=1,lz2[p]=0;}
	inline void build(int p,int l,int r){
		lz1[p]=1,lz2[p]=0,len[p]=r-l+1;
		if(l==r){exi[p]=solve((val[p]=read()));return;}
		build(lc,l,mid),build(rc,mid+1,r),pushup(p);
	}
	inline void update(int p,int l,int r,int ql,int qr,int v1,ll v2){
		if(ql<=l&&r<=qr)return pushnow(p,v1,v2);
		pushdown(p);
		if(qr<=mid)update(lc,l,mid,ql,qr,v1,v2);
		else if(ql>mid)update(rc,mid+1,r,ql,qr,v1,v2);
		else update(lc,l,mid,ql,mid,v1,v2),update(rc,mid+1,r,mid+1,qr,v1,v2);
		pushup(p);
	}
	inline pii query(int p,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr)return pii(val[p],exi[p]);
		pushdown(p);
		if(qr<=mid)return query(lc,l,mid,ql,qr);
		if(ql>mid)return query(rc,mid+1,r,ql,qr);
		pii a=query(lc,l,mid,ql,mid),b=query(rc,mid+1,r,mid+1,qr);
		return pii(mul(a.fi,b.fi),a.se|b.se);
	}
}
inline int calc(int v1,ll v2){ 
	for(ri i=0;i<=tot;++i)if((v2>>i)&1)v1=mul(v1,mul(pri[i]-1,invp[i]));
	return v1;
}
int main(){
	init(),n=read(),q=read();
	SGT::build(1,1,n);
	char s[10];
	for(ri tt=q,l,r,x;tt;--tt){
		scanf("%s",s),l=read(),r=read();
		if(s[0]=='T'){
			pii tmp=SGT::query(1,1,n,l,r);
			cout<<calc(tmp.fi,tmp.se)<<'\n';
		}
		else x=read(),SGT::update(1,1,n,l,r,x,solve(x));
	}
	return 0;
}

转载于:https://www.cnblogs.com/ldxcaicai/p/10367690.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值