// 为了给学弟学妹更好的理解,我尽量写详细一点。
A. Reachable Numbers
题意:设f(x)为 x+1 这个数去掉后缀0的数,现在给出n,问经过无数次这种变换后,最多能得到多少个不同的数。
思路:我们用一个数组标记用过的数x,然后开始循环变换x,直到x变成一个曾经用过的数就结束,那么问题来了,x最大可能有1e9,普通数组标记不了,这个时候我们可以用map来标记 x,stl 的 map原理是红黑树,我就不详细讲了,相关数据结构就是二叉查找树,你们可以了解下。
#include
using namespace std;
mapmp;
int main()
{
int x,res=0;
cin>>x;
while(!mp[x])
{
res++;
mp[x]=1;
x++;
while(x%10==0)
x/=10;
}
cout<
B. Long Number
题意:给一个长度为n的字符串,给出 f 数组表示你可以把字符 x 变成对应的 f (x),你只能修改一段连续的子串,求修改后字典序最大的字符串。什么是字典序大家可以百度了解一下,我不详细讲。
思路:字典序最大,那么我们肯定优先从左边开始枚举,我们找到第一个 si < f(si) 的位置 p ,那么显然从这里开始修改 si 肯定是字典序最小的,我们一直修改直到 sj > f(sj)就结束。
#include
using namespace std;
const int maxn=2e5+10;
char s[maxn];
int vis[maxn];
int main()
{
int n;
cin>>n>>s+1;
for(int i=1;i<=9;i++)
cin>>vis[i];
for(int i=1;i<=n;i++)
s[i]-='0';
for(int i=1;i<=n;i++)
if(s[i]
C1. Increasing Subsequence (easy version)
题意:给一个长度为 n 的数组,每个数不一样,每次你可以从坐边界或者右边界取一个数,要求每一次取的数都要比上一次取得数要大,求最多可以取多少个数。
思路:我们用两个指针 p , q 分别表示当前的左边界,右边界,我们记一下上一次取得数为 x(初始化为0),假设当前左边界 <右边界,显然优先选左边界更优,但是我们因为 x 的存在,需要特判,如果 x < ap,那么就取ap,否则我们看 aq 是否大于 x,如果大于,那么就取aq,否则就结束了。
#include
using namespace std;
const int maxn=2e5+10;
int a[maxn],cnt;
char s[maxn];
int main()
{
int n;
scanf("%d",&n);
int p=1,q=n,x=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
while(p<=q)
{
if(a[p]x)
{
s[++cnt]='L';
x=a[p++];
}
else if(a[q]>x)
{
s[++cnt]='R';
x=a[q--];
}
else break;
}
else
{
if(a[q]>x)
{
s[++cnt]='R';
x=a[q--];
}
else if(a[p]>x)
{
s[++cnt]='L';
x=a[p++];
}
else break;
}
}
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++)
printf("%c",s[i]);
}
C2. Increasing Subsequence (hard version)
题意:同上,不过新加条件就是有可能有多个数是一样的(即有可能 ai = aj)。
思路:同上,不过假设当前我们的左右边界如果相等了,比如 2 3 4 5 4 2,假设我们已经取了 ans 个数,这个时候我们看一下从左边这个2最多能取多长,我们发现最多能取4个数,右边这个2显然只能取2个数,那么答案就是ans+4。
#include
using namespace std;
const int maxn=2e5+10;
int a[maxn],cnt;
char s[maxn];
int main()
{
int n;
scanf("%d",&n);
int p=1,q=n,x=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
while(p<=q)
{
if(a[p]x)
{
s[++cnt]='L';
x=a[p++];
}
else if(a[q]>x)
{
s[++cnt]='R';
x=a[q--];
}
else break;
}
else if(a[p]>a[q])
{
if(a[q]>x)
{
s[++cnt]='R';
x=a[q--];
}
else if(a[p]>x)
{
s[++cnt]='L';
x=a[p++];
}
else break;
}
else
{
if(a[p]<=x)break;
int t1=1,t2=1;
for(int i=p+1;ia[i-1])t1++;
else break;
for(int i=q-1;i>p;i--)
if(a[i]>a[i+1])t2++;
else break;
if(t1>=t2)
{
for(int i=1;i<=t1;i++)
s[++cnt]='L';
}
else
{
for(int i=1;i<=t2;i++)
s[++cnt]='R';
}
break;
}
}
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++)
printf("%c",s[i]);
}
D. N Problems During K Days
题意:你要设计一个长度为 k 的数组a,a1 你可以设置为任何数,要求 ai>ai-1 且 ai<=2*ai-1,a数组的和要刚好等于n。
思路:我们先二分确定好起点,假设我们设置起点为 x,那么这个数组最小的和就是x+(x+1)+(x+2)...也就是等差数列求和,最大的和就是等比数列求和,我们先二分找一个最小的x,使得等比数列求和 >= n,然后再判断等差数列求和是否 <=n,如果 >n 显然无解,有过有解,那我们就开始输出 x,然后 k-=1,n-=x,然后接下来继续二分找一个比刚刚的 x 大一点的 x即可。
#include
#define ll long long
using namespace std;
const int maxn=2e5+10;
int a[maxn];
ll n,k;
int ok(ll x)
{
ll res = 1<res*x)return 0;
return 1;
}
void dfs(int x)
{
if(k==1)
{
printf("%lld\n",n);
return;
}
printf("%d ",x);
n-=x;
k--;
int l=x+1,r=x*2;
while(l>n>>k;
int l=1,r=n;
while(ln)puts("NO");
else
{
puts("YES");
dfs(l);
}
}
E. Minimum Array
思路:给你一个a数组,一个b数组,定义ci = (ai + bi)%n,你可以重新排列b数组,使得ci字典序最小,我们先把 bi 存起来(可以用multiset,我用的线段树),枚举 ai,令 x = (n-ai)%n,然后我们先在[x, n-1]的区间(这个区间指的是权值不是下标)找一个最小的没用过的bj,(用数据结构实现,multi 或者线段树,复杂度logn),如果没找到,那我们继续在[ 0 , x-1 ]这个区间继续找最小的没用过的bj 即可,然后标记bj,答案就是 (ai+bj)%n。
#include
using namespace std;
const int maxn=2e5+10;
int mx[maxn*4],mn[maxn*4],inf=1e9;
int a[maxn],b[maxn];
void up(int o,int l,int r,int k,int v)
{
if(l==r)
{
mn[o]=v;
return;
}
int m=(l+r)/2,ls=o*2,rs=o*2+1;
if(k<=m)up(ls,l,m,k,v);
else up(rs,m+1,r,k,v);
mx[o]=max(mx[ls],mx[rs]);
mn[o]=min(mn[ls],mn[rs]);
}
int qmin(int o,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)return mn[o];
int m=(l+r)/2,ls=o*2,rs=o*2+1,res=1e9;
if(ql<=m)res=min(res,qmin(ls,l,m,ql,qr));
if(qr>m)res=min(res,qmin(rs,m+1,r,ql,qr));
return res;
}
int main()
{
int n,x;
scanf("%d",&n);
for(int i=0;i
F. Maximum Balanced Circle
题意:给你一个长度为 n 的 a 数组,你可以从中选择尽量多的元素,重新排列,使得相邻两个数相差不超过1,首尾元素也相差不超过1。
思路:仔细分析一下我们发现,假设有这样的数 1 2 2 3 3 3 4 5 5,那么答案肯定就是7,也就是我们按顺序(每次+1的顺序),找到第一个不为0的数(出现次数不为0),然后再继续找到第一个出现次数<=1的数,那么这段区间的所有数都是能组成合法的数组的,然后我们和答案去max一下就好了。
#include
#define ll long long
using namespace std;
const int maxn=2e5+10,N=2e5;
int a[maxn],b[maxn];
int main()
{
int n,x;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>x;
a[x]++;
}
if(n==1)
{
printf("%d\n%d",1,n);
return 0;
}
int ans=1;
int p1,p2,t1,t2;
int p=1;
while(p<=N)
{
while(!a[p]&&p<=N)
p++;
p1=p;
int res=a[p];
p++;
while(a[p]>1&&p<=N)
res+=a[p],p++;
res+=a[p];
p2=p;
if(res>ans)
{
ans=res;
t1=p1,t2=p2;
}
}
printf("%d\n",ans);
p1=1,p2=1;
for(int i=t1;i<=t2;i++)
for(int j=1;j<=a[i];j++)
{
if(j==1)
{
b[p1]=i;
p1--;
if(p1<1)p1+=ans;
}
else
{
++p2;
b[p2]=i;
}
}
for(int i=1;i<=ans;i++)
printf("%d ",b[i]);
}