A
题意
给出 a,b,c ,取3个数满足 0≤ca≤a,0≤cb≤b,0≤cc≤c,ca:cb:cc=1:2:4 ,并且使 ca,cb,cc 的总和最大。如果无法满足,输出0.
数据范围
0≤a,b,c≤1000
题解
随便乱搞搞…
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
using namespace std;
int a,b,c,ans;
int main()
{
scanf("%d%d%d",&a,&b,&c);
for (int i=a;i>=1;--i)
if (i*2<=b&&i*4<=c) {ans=i+i*2+i*4;break;}
printf("%d\n",ans);
}
B
题意
一个长度为
n
的字符串的中间字符定义为
数据范围
1≤n≤2000
题解
根据奇偶的不同加加减减
随便乱搞搞…
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
using namespace std;
#define N 5005
int n,now;
char s[N],ans[N];
int main()
{
scanf("%d\n",&n);gets(s+1);
now=(n-1)/2+1;
for (int i=1;i<=n;++i)
{
ans[now]=s[i];
if (n%2==0)
{
if (i%2) now+=i;
else now-=i;
}
else
{
if (i%2) now-=i;
else now+=i;
}
}
for (int i=1;i<=n;++i) putchar(ans[i]);
putchar('\n');
}
C
题意
数据范围
2≤s≤100,0≤x1,x2≤s,x1≠x2,1≤t1,t2≤1000,1≤p≤s−1,0≤d≤1
题解
很显然这道题只是比较人和车谁跑得快而已…
那么根据
x1,x2,p
的位置以及方向
d
分类讨论计算就可以了。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
using namespace std;
int s,x1,x2,t1,t2,p,d,ans1,ans2,ans;
int main()
{
scanf("%d%d%d",&s,&x1,&x2);
scanf("%d%d",&t1,&t2);
scanf("%d%d",&p,&d);
if (x1==x2)
{
puts("0");
return 0;
}
if (x1<x2)
{
ans1=(x2-x1)*t2;
if (d==1)
{
if (p<=x1) ans2=(x2-p)*t1;
else ans2=(s-p+s+x2)*t1;
}
else ans2=(p+x2)*t1;
}
else
{
ans1=(x1-x2)*t2;
if (d==1)
ans2=(s-p+s-x2)*t1;
else
{
if (p>=x1) ans2=(p-x2)*t1;
else ans2=(p+s+s-x2)*t1;
}
}
ans=min(ans1,ans2);
printf("%d\n",ans);
}
D
题意
给出a个‘G’b个‘B’共n个字母,将他们排列,问是否存在一种方案使得这两个字母连续都不超过k个。
数据范围
题解
构造体吖
首先如果a=b特判
然后先将所有少的分散来放,再将多的塞在中间,都只塞一个,形成类似GBGBG这种形状,然后再把多余的多的塞到可以塞的位置,要保证合法。这样做可以防止之前放多了后来不够的情况。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
using namespace std;
#define N 100005
int n,k,a,b;
bool flag;
int cnt[N];
char A,B;
int main()
{
scanf("%d%d%d%d",&n,&k,&a,&b);
if (a==b)
{
for (int i=1;i<=n;++i)
if (i%2) putchar('G');
else putchar('B');
putchar('\n');
return 0;
}
if (a<b) swap(a,b),flag=1;
for (int i=1;i<=2*b+1;++i) cnt[i]=1;
a-=b+1;
for (int i=1;i<=2*b+1;i+=2)
{
if (a>=k-1)
{
cnt[i]+=k-1;
a-=k-1;
}
else
{
cnt[i]+=a;
a=0;
}
if (!a) break;
}
if (a)
{
puts("NO");
return 0;
}
if (!flag) A='G',B='B';
else A='B',B='G';
for (int i=1;i<=2*b+1;++i)
{
if (i%2)
{
while (cnt[i]--) putchar(A);
}
else
{
while (cnt[i]--) putchar(B);
}
}
putchar('\n');
}
E
题意
给出一列n个数,问能否从1~m这些数中选出一些数替换掉这n个数中的若干数使得这n个数中奇数和偶数的个数相等并且没有重复出现的数。如果可以,输出最少替换个数和方案,否则-1.
数据范围
2≤n≤2∗105,1≤m≤109,1≤ai≤109
题解
其实这道题没什么难的,就是有点麻烦。
首先统计出来要换多少个数,然后分奇偶找就可以了。需要注意的是不需要替换的数尽量找大的,这样1~m的限制更容易被满足,是一个贪心的思想。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
using namespace std;
#define N 200005
int n,m;
struct hp{int val,id;bool flag;}a[N];
int odd[N],even[N],cnt,cnto,cnte,anso[N],anse[N];
int cmpval(hp a,hp b)
{
return a.val>b.val;
}
int cmpid(hp a,hp b)
{
return a.id<b.id;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i) scanf("%d",&a[i].val),a[i].id=i;
sort(a+1,a+n+1,cmpval);
a[0].val=0;odd[++odd[0]]=-1;even[++even[0]]=0;
for (int i=1;i<=n;++i)
if (a[i].val!=a[i-1].val)
{
if (a[i].val%2)
{
odd[++odd[0]]=a[i].val;
if (odd[0]-1<=n/2) a[i].flag=true;
}
else
{
even[++even[0]]=a[i].val;
if (even[0]-1<=n/2) a[i].flag=true;
}
}
if (odd[0]-1<=n/2) cnto=n/2-odd[0]+1;
if (even[0]-1<=n/2) cnte=n/2-even[0]+1;
cnt=cnto+cnte;
sort(odd+1,odd+odd[0]+1);
sort(even+1,even+even[0]+1);
for (int i=2;i<=odd[0];++i)
{
if (!cnto) break;
for (int j=odd[i-1]+2;j<odd[i];j+=2)
{
anso[++anso[0]]=j;
--cnto;
if (!cnto) break;
}
}
for (int i=odd[odd[0]]+2;i<=m;i+=2)
{
if (!cnto) break;
anso[++anso[0]]=i;
--cnto;
}
if (cnto)
{
puts("-1");
return 0;
}
for (int i=2;i<=even[0];++i)
{
if (!cnte) break;
for (int j=even[i-1]+2;j<even[i];j+=2)
{
anse[++anse[0]]=j;
--cnte;
if (!cnte) break;
}
}
for (int i=even[even[0]]+2;i<=m;i+=2)
{
if (!cnte) break;
anse[++anse[0]]=i;
--cnte;
}
if (cnte)
{
puts("-1");
return 0;
}
for (int i=1;i<=n;++i)
if (!a[i].flag)
{
if (anso[0]) a[i].val=anso[anso[0]--];
else a[i].val=anse[anse[0]--];
}
sort(a+1,a+n+1,cmpid);
printf("%d\n",cnt);
for (int i=1;i<=n;++i)
printf("%d%c",a[i].val," \n"[i==n]);
}
F
题意
有
n
首歌,每个歌有一个喜悦值
数据范围
1 ≤ w ≤ n ≤ 2⋅105,1 ≤ k ≤ 2⋅109,1 ≤ ai ≤ 104,2 ≤ ti ≤ 104
题解
这道题可以用左右两个指针来解决,也就是单调地移动区间。
首先脑子正常的人都明白只要在
w
之内能听一半就听一半一定是更优的。如果区间长度小于或等于
但是不能跳歌,也就是说如果一首歌无论如何都听不了的话后面的也就听不了了。所以无论何时端点移动的时候都必须满足要么区间里的所有歌都听一半,
w
可以有剩余,要么听区间里的前
这样每一次找最大值和最小值替换的时候就需要用到数据结构来维护,线段树就可以。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
using namespace std;
#define N 200005
int n,w,k;
int inf,l,r,sum,val,ans;
int a[N],f[N],_f[N],h[N],_h[N];
namespace Min
{
int minn[N*4];
void update(int now)
{
if (_h[minn[now<<1]]<=_h[minn[now<<1|1]])
minn[now]=minn[now<<1];
else minn[now]=minn[now<<1|1];
}
void build(int now,int l,int r)
{
int mid=(l+r)>>1;
if (l==r)
{
minn[now]=l;
return;
}
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
void change(int now,int l,int r,int x)
{
int mid=(l+r)>>1;
if (l==r)
{
minn[now]=x;
return;
}
if (x<=mid) change(now<<1,l,mid,x);
else change(now<<1|1,mid+1,r,x);
update(now);
}
int query(int now,int l,int r,int lrange,int rrange)
{
if (lrange>rrange) return 0;
int mid=(l+r)>>1,ans=0;
if (lrange<=l&&r<=rrange) return minn[now];
if (lrange<=mid)
{
int q=query(now<<1,l,mid,lrange,rrange);
if (_h[ans]>_h[q]) ans=q;
}
if (mid+1<=rrange)
{
int q=query(now<<1|1,mid+1,r,lrange,rrange);
if (_h[ans]>_h[q]) ans=q;
}
return ans;
}
}
namespace Max
{
int maxn[N*4];
void update(int now)
{
if (_f[maxn[now<<1]]>=_f[maxn[now<<1|1]])
maxn[now]=maxn[now<<1];
else maxn[now]=maxn[now<<1|1];
}
void build(int now,int l,int r)
{
int mid=(l+r)>>1;
if (l==r)
{
maxn[now]=l;
return;
}
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
void change(int now,int l,int r,int x)
{
int mid=(l+r)>>1;
if (l==r)
{
maxn[now]=x;
return;
}
if (x<=mid) change(now<<1,l,mid,x);
else change(now<<1|1,mid+1,r,x);
update(now);
}
int query(int now,int l,int r,int lrange,int rrange)
{
if (lrange>rrange) return 0;
int mid=(l+r)>>1,ans=0;
if (lrange<=l&&r<=rrange) return maxn[now];
if (lrange<=mid)
{
int q=query(now<<1,l,mid,lrange,rrange);
if (_f[ans]<_f[q]) ans=q;
}
if (mid+1<=rrange)
{
int q=query(now<<1|1,mid+1,r,lrange,rrange);
if (_f[ans]<_f[q]) ans=q;
}
return ans;
}
}
int main()
{
scanf("%d%d%d",&n,&w,&k);
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
for (int i=1;i<=n;++i)
{
scanf("%d",&f[i]);
_f[i]=f[i];
h[i]=(f[i]-1)/2+1;
}
memset(_h,127,sizeof(_h));inf=_h[0];
Max::build(1,1,n);
Min::build(1,1,n);
while (l<=n)
{
if (l<=r)
{
val-=a[l];
if (_h[l]!=inf)
{
sum-=h[l];
int loc=Max::query(1,1,n,l,r);
if (loc)
{
sum-=f[loc]-h[loc];
_f[loc]=0;_h[loc]=h[loc];
Max::change(1,1,n,loc);Min::change(1,1,n,loc);
}
else ++w;
}
else sum-=f[l];
}
++l;
r=max(r,l-1);
while (r<n)
{
if (w)
{
if (sum+h[r+1]<=k)
{
sum+=h[r+1];
_f[r+1]=0;_h[r+1]=h[r+1];
Max::change(1,1,n,r+1);
Min::change(1,1,n,r+1);
--w;
val+=a[++r];
}
else break;
}
else
{
int loc=Min::query(1,1,n,l,r);
if (loc&&f[loc]-h[loc]<f[r+1]-h[r+1]&&sum-h[loc]+f[loc]+h[r+1]<=k)
{
sum+=f[loc]-h[loc];
_f[loc]=f[loc];_h[loc]=inf;
Max::change(1,1,n,loc);Min::change(1,1,n,loc);
sum+=h[r+1];
_f[r+1]=0;_h[r+1]=h[r+1];
Max::change(1,1,n,r+1);Min::change(1,1,n,r+1);
val+=a[++r];
}
else if (sum+f[r+1]<=k)
{
sum+=f[r+1];
val+=a[++r];
}
else break;
}
}
ans=max(ans,val);
}
printf("%d\n",ans);
}
G
题意
构造一棵有
n
个节点,以1为根的树,满足共有
数据范围
1≤n≤2∗105,1≤t,k<n,1≤ai<n
题解
怎么又是一道构造题…
首先如果这一层的节点数
ai
比下一层
ai+1
要多,那么这一层一定会有
ai−ai+1
个叶子节点。
最后一层的节点一定都是叶子节点。
那么我们可以计算出来最少的叶子节点有多少个,然后指定一些为一定为叶子节点。然后在剩余的点中建树,如果叶子节点不够的话就把儿子都往一个父亲上连,产生一些叶子节点,如果够了的话就一个儿子连一个父亲。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
using namespace std;
#define N 200005
int n,t,k,must,last,now;
int a[N],b[N],s[N],f[N];
int main()
{
scanf("%d%d%d",&n,&t,&k);a[0]=s[0]=1;
for (int i=1;i<=t;++i)
{
scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];
if (a[i]<a[i-1]) must+=a[i-1]-a[i],b[i-1]=a[i-1]-a[i];
}
must+=a[t];
if (s[t]!=n||must>k)
{
puts("-1");
return 0;
}
k-=must;
last=1,now=2;
for (int i=1;i<=t;++i)
{
if (k<=a[i-1]-1-b[i-1])
{
last+=k;
while (last<=s[i-1]-b[i-1]&&now<=s[i])
{
f[now]=last;
++now,++last;
}
while (now<=s[i])
{
f[now]=last-1;
++now;
}
k=0;
}
else
{
last+=a[i-1]-1-b[i-1];
while (last<=s[i-1]-b[i-1]&&now<=s[i])
{
f[now]=last;
++now,++last;
}
while (now<=s[i])
{
f[now]=last-1;
++now;
}
k-=a[i-1]-1-b[i-1];
}
last=s[i-1]+1;
now=s[i]+1;
}
if (k)
{
puts("-1");
return 0;
}
printf("%d\n",n);
for (int i=2;i<=n;++i) printf("%d %d\n",f[i],i);
return 0;
}