Codeforces Round #538 (Div. 2)

A

签到题,略

 

 

B

题意:

给定长度为n的序列,将其分成k个长度大于m的段,要求最大化 每段前m大的元素的和的和

题解:

显然取全局前m*k大是最优的,根据这m*k大元素将序列分成k段即可

代码:

/**
 *          ┏┓    ┏┓
 *          ┏┛┗━━━━━━━┛┗━━━┓
 *          ┃       ┃  
 *          ┃   ━    ┃
 *          ┃ >   < ┃
 *          ┃       ┃
 *          ┃... ⌒ ...  ┃
 *          ┃              ┃
 *          ┗━┓          ┏━┛
 *          ┃          ┃ Code is far away from bug with the animal protecting          
 *          ┃          ┃   神兽保佑,代码无bug
 *          ┃          ┃           
 *          ┃          ┃        
 *          ┃          ┃
 *          ┃          ┃           
 *          ┃          ┗━━━┓
 *          ┃              ┣┓
 *          ┃              ┏┛
 *          ┗┓┓┏━━━━━━━━┳┓┏┛
 *           ┃┫┫       ┃┫┫
 *           ┗┻┛       ┗┻┛
 */ 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<set>
#include<bitset>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y>>1)
#define NM 200005
#define nm 200005
#define pi 3.1415926535897931
const int inf=1e9+7;
using namespace std;
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}
 
 






int n,m,k,a[NM],tmp[NM],x;
ll ans;
bool v[NM];
bool cmp(int x,int y){return a[x]>a[y];}

int main(){
    n=read();m=read();k=read();
    inc(i,1,n)a[i]=read(),tmp[i]=i;
    sort(tmp+1,tmp+1+n,cmp);
    inc(i,1,m*k)v[tmp[i]]++,ans+=a[tmp[i]];
    printf("%lld\n",ans);
    inc(i,1,k){
	inc(j,1,m){
	    x++;
	    while(x<=n&&!v[x])x++;
	}
	if(i<k)printf("%d ",x);
    }
    putchar('\n');
    return 0;
}

 

 

C

题意:

求出n!在b进制下的末尾0的个数

题解:

参照10进制末尾0的算法,对b进行素因子分解,然后统计n!中每个素因子的数量,考虑到p^k的存在,所以要做一点小容斥

注意部分地方爆ll

代码:

/**
 *          ┏┓    ┏┓
 *          ┏┛┗━━━━━━━┛┗━━━┓
 *          ┃       ┃  
 *          ┃   ━    ┃
 *          ┃ >   < ┃
 *          ┃       ┃
 *          ┃... ⌒ ...  ┃
 *          ┃              ┃
 *          ┗━┓          ┏━┛
 *          ┃          ┃ Code is far away from bug with the animal protecting          
 *          ┃          ┃   神兽保佑,代码无bug
 *          ┃          ┃           
 *          ┃          ┃        
 *          ┃          ┃
 *          ┃          ┃           
 *          ┃          ┗━━━┓
 *          ┃              ┣┓
 *          ┃              ┏┛
 *          ┗┓┓┏━━━━━━━━┳┓┏┛
 *           ┃┫┫       ┃┫┫
 *           ┗┻┛       ┗┻┛
 */ 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<set>
#include<bitset>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y>>1)
#define NM 200005
#define nm 200005
#define pi 3.1415926535897931
const ll inf=9e18;
using namespace std;
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}
 
 







ll n,m,a[NM],cnt[NM],ans,tot;

int main(){
    n=read();m=read();
    for(ll i=2;i*i<=m;i++)if(m%i==0){
	a[++tot]=i;while(m%i==0)m/=i,cnt[tot]++;
    }
    if(m>1)a[++tot]=m,cnt[tot]=1;
    ans=inf;
    inc(i,1,tot){
	ll s=0;
	for(ll k=a[i],j=1;k<=n;k*=a[i],j++)
	    if(n/k>=a[i])s+=j*(n/k-n/(k*a[i]));
	else{
	    s+=j*(n/k);
	    break;
	}
	ans=min(ans,s/cnt[i]);
    }
    return 0*printf("%lld\n",ans);
}

 

 

D

题意:

将连续的有相同数字的段称为一个联通块

从某一个位置出发,将其所在的联通块的所有数字,变成任意一个数字,问最少用多少步能使得整个序列变成一个联通块

题解:

先对序列去重(去重之后长度为n),考虑个个数字两两不同的情况,那么需要n-1次操作,更少的操作得益于类似212的情况,即2侧的联通块的数字相同的情况,出现一次操作数减一,因此需要枚举每个点出发,看2侧能够形成的配对对有多少就行

其实这个配对对就是将枚举点一侧翻转过来求LCS,那么直接整体翻转后求LCS,再来枚举匹配点就可以了

代码:

/**
 *          ┏┓    ┏┓
 *          ┏┛┗━━━━━━━┛┗━━━┓
 *          ┃       ┃  
 *          ┃   ━    ┃
 *          ┃ >   < ┃
 *          ┃       ┃
 *          ┃... ⌒ ...  ┃
 *          ┃              ┃
 *          ┗━┓          ┏━┛
 *          ┃          ┃ Code is far away from bug with the animal protecting          
 *          ┃          ┃   神兽保佑,代码无bug
 *          ┃          ┃           
 *          ┃          ┃        
 *          ┃          ┃
 *          ┃          ┃           
 *          ┃          ┗━━━┓
 *          ┃              ┣┓
 *          ┃              ┏┛
 *          ┗┓┓┏━━━━━━━━┳┓┏┛
 *           ┃┫┫       ┃┫┫
 *           ┗┻┛       ┗┻┛
 */ 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<set>
#include<bitset>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y>>1)
#define NM 5005
#define nm 200005
#define pi 3.1415926535897931
const ll inf=9e18;
using namespace std;
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}
 
 








int a[NM],b[NM],d[NM][NM],ans,n,m;

int main(){
    n=read();inc(i,1,n)a[i]=read();
    inc(i,1,n)if(a[i]!=a[i+1])b[++m]=a[i];
    n=m;inc(i,1,n)a[i]=b[i];
    reverse(b+1,b+1+n);
    inc(i,1,n)inc(j,1,n){
	d[i][j]=max(d[i][j-1],d[i-1][j]);
	if(a[i]==b[j])d[i][j]=max(d[i][j],d[i-1][j-1]+1);
    }
    inc(i,1,n-1)ans=max(ans,d[i][n-i]);
    return 0*printf("%d\n",n-1-ans);
}

 

 

E

题意:

给出一个等差数列将其乱序,你有2种询问操作:

1.“? i",询问ai的值

2.">  x",询问序列中是否有元素大于x

要在60次询问内询问出该序列的首项和公差

题解:

显然二分30次可以查出序列的最大值,还要用30次得到等差的另一个信息,其实可以随机选30个数询问然后得到差值求gcd得到d。如果数据随机,那么相当于在0~n-1的排列中选30个数使得这些数的gcd为1,这个其实还是比较好做到的。

然后关键在怎么随机(

如果能用时间随机种子这题估计场上就过了。。

不能的话自己弄一个随机种子吧。。有被对着卡的风险。。(好在现在没人卡泥了

要问有没有靠谱点的做法->黑科技介绍:mt19937

代码 :

/**
 *          ┏┓    ┏┓
 *          ┏┛┗━━━━━━━┛┗━━━┓
 *          ┃       ┃  
 *          ┃   ━    ┃
 *          ┃ >   < ┃
 *          ┃       ┃
 *          ┃... ⌒ ...  ┃
 *          ┃              ┃
 *          ┗━┓          ┏━┛
 *          ┃          ┃ Code is far away from bug with the animal protecting          
 *          ┃          ┃   神兽保佑,代码无bug
 *          ┃          ┃           
 *          ┃          ┃        
 *          ┃          ┃
 *          ┃          ┃           
 *          ┃          ┗━━━┓
 *          ┃              ┣┓
 *          ┃              ┏┛
 *          ┗┓┓┏━━━━━━━━┳┓┏┛
 *           ┃┫┫       ┃┫┫
 *           ┗┻┛       ┗┻┛
 */ 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<set>
#include<bitset>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y>>1)
#define NM 5005
#define nm 200005
#define pi 3.1415926535897931
const int inf=1e9+7;
using namespace std;
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}
 
 







int n,ans,s,cnt,t;

int main(){
    srand(19260817);
    scanf("%d",&n);
    for(int x=0,y=1e9-1;x<=y;){
	printf("> %d\n",mid);
	fflush(stdout);
	s++;
	scanf("%d",&t);
	if(t)ans=x=mid+1;else y=mid-1;
    }
    cnt=0;
    inc(i,1,min(60-s,n)){
	printf("? %d\n",(rand()*rand()+rand())%n+1);
	fflush(stdout);
	scanf("%d",&t);
	cnt=__gcd(cnt,ans-t);
    }
    printf("! %d %d\n",ans-(n-1)*cnt,cnt);
    return 0;
}

 

F

泥萌的汪聚聚一上来就把f切了

链接:https://www.cnblogs.com/wang9897/p/10360754.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值