AtCoder-arc060 (题解)
Posted c-w-k
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder-arc060 (题解)相关的知识,希望对你有一定的参考价值。
A - 高橋君とカード / Tak and Cards (DP)
题目大意:
有 (n) 个数字,要求取出一些数字,使得它们的平均数恰好为 (x) ,问有几种取法。
大致思路:
只要将每一个数字减掉 (x) ,那么问题就变成在 (n) 个数字中选取一些数字使得和为 (0) 的方案数,是一个经典的 (dp) 问题,不过要注意细节问题
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5010;
const int Y=2500;
ll dp[N][N];
int n,x;
int a[N];
int main()
{
//freopen("H:\c++1\in.txt","r",stdin);
//freopen("H:\c++1\out.txt","w",stdout);
scanf("%d%d",&n,&x);
dp[0][Y]=1;
for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]-=x;
for(int i=1;i<=n;i++){
for(int j=-Y;j<=Y;j++){
dp[i][j+Y+a[i]]+=dp[i-1][j+Y];
dp[i][j+Y]+=dp[i-1][j+Y];
}
}
printf("%lld
",dp[n][Y]-1); //记得减一
return 0;
}
B - 桁和 / Digit Sum (思维)
题目大意:
定义 (f(n,b)) 为 (n) 在 (b) 进制下各位数之和,现在给定 (,n,s) ,为求得最小的 (b) 使得 (f(n,b)=s)
((n,s<=1e11))
大致思路:
这题很巧妙,首先如果答案 (b) 小于 (1e6) ,那么直接暴力枚举即可,若 (b>1e6) ,那么由于 (n<=1e11) ,那么 (n) 必然可以写成 (n=kb+r) ,且 (s=k+r) ,的形式, (n-s=k(b-1)) ,只要枚举 (n-s) 的因数即可解决,思维好题。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,s;
bool check(ll x){
if(n/x>=x)return 0;
if(x<2)return 0;
ll k=n/x;ll r=n%x;
if(s==k+r)return 1;
return 0;
}
ll js(ll x,ll b){
ll ans=0;
while(x){
ans+=x%b;
x/=b;
}
return ans;
}
int main()
{
//freopen("H:\c++1\in.txt","r",stdin);
//freopen("H:\c++1\out.txt","w",stdout);
cin>>n>>s;
ll b=1e18;
if(n==s)b=n+1;
if(n>s){
ll temp=n-s;
for(ll t=1;t<=sqrt(temp);t++){
if(temp%t==0){
ll x=t+1,y=temp/t+1;
if(check(x))b=min(b,x);
if(check(y))b=min(b,y);
}
}
}
for(ll t=2;t<=1000000;t++){
ll cnt=js(n,t);
if(cnt==s){
b=min(b,t);
break;
}
}
if(b==1e18)printf("-1
");
else printf("%lld
",b);
return 0;
}
E - 高橋君とホテル / Tak and Hotels (倍增)
题目大意:
水平轴上有 (n) 个点,每次跳不能超过 (L) ,每次必须跳在点上, (Q) 次询问,每次询问 (,x,y) ,表示从 (x) 到 (y) 最少需要跳几下。
大致思路:
感觉这题比较常见,是倍增的经典套路,用二分处理出 (dp[i][0]) ,跑一下预处理,然后就每次询问log查询就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int dp[N][23];
int n,L;
int a[N];
int q,x,y;
int ef(int id,int v){
int l=id,r=n,ans=id;
while(l<=r){
int mid=(l+r)/2;
if(a[mid]<=v)ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
void cl(){ //
for(int i=1;i<=n;i++)dp[i][0]=ef(i,a[i]+L);
for(int j=1;j<23;j++){
for(int i=1;i<=n;i++){
dp[i][j]=dp[dp[i][j-1]][j-1];
}
}
}
int js(int x,int y){
int ans=0,pos=x;
for(int i=22;i>=0;i--){
if(dp[pos][i]<y){
pos=dp[pos][i];
ans+=(1<<i);
}
}
ans++;
return ans;
}
int main()
{
//freopen("H:\c++1\in.txt","r",stdin);
//freopen("H:\c++1\out.txt","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
scanf("%d",&L);
scanf("%d",&q);
cl();
while(q--){
scanf("%d%d",&x,&y);
if(x>y)swap(x,y);
printf("%d
",js(x,y));
}
return 0;
}
D - 最良表現 / Best Representation (KMP)
题意大意:
定义一个字符串合法:当该字符串没有循环节存在,给出 (string s) ,令 (F=(f_1,f_2..f_m)) 满足 (f_i) 为 (s) 的某一部分. (f_1,f_2,..f_m) 连起来为 (s) .并且任意 (f_i) 为合法
(|s|<=5e5),求出 (F) 表示中最小的 (m) .并求出最小 (m) 的方案数?
大致思路:
这题感觉不难,只是由于是最后一题心理上有些畏惧,可惜了,首先先求出字符串的循环节,如果不存在循环节那么答案就是 (1 1) ,特判每一个字母都相同的情况答案为 (len 1) ,比较显然,那么我们可以确定剩下的字符串必然可以分成两个好串,那么我们只要枚举断点用 (kmp) 判断前面的字符串和后面的字符串是否为好串即可,代码实现也比较简单。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
char s[N];
char s1[N];
int nxt[N],nxt1[N];//next数组
void kmp(char *t,int *nxt){//t为去匹配,s为被匹配
int i,j;
int len1=strlen(t+1);
nxt[0]=nxt[1]=0;
for(i=2,j=0;i<=len1;i++){
while(j&&t[j+1]!=t[i])j=nxt[j];
if(t[j+1]==t[i])++j;
nxt[i]=j;
}
}
bool check(int x,int len){
int f1,f2;
int l1=x-nxt[x],l2=len-x-(nxt1[len-x]);
//if(x==2)cout<<l1<<" "<<l2<<endl;
if(x==1||l1==x||x%l1!=0)f1=1;
else f1=0;
if(len-x==1||l2==len-x||(len-x)%l2!=0)f2=1;
else f2=0;
return (f1&&f2);
}
int main()
{
//freopen("H:\c++1\in.txt","r",stdin);
//freopen("H:\c++1\out.txt","w",stdout);
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=1;i<=len;i++){
s1[i]=s[len-i+1];
}
kmp(s,nxt);kmp(s1,nxt1);
int temp=len-nxt[len];
if(temp==1){
printf("%d
%d
",len,1);return 0;
}
if(temp!=len&&len%temp==0){
int ans=0;
for(int i=1;i<len;i++){
if(check(i,len))ans++;
}
printf("2
%d
",ans);
}
else{
printf("1
1
");
}
return 0;
}
以上是关于AtCoder-arc060 (题解)的主要内容,如果未能解决你的问题,请参考以下文章