参考博客:10月19日训练记录 CF GYM 101911
A_Coffee Break
【题意】:
一个打工仔,想工作时候放松一下,但是又担心老板说他偷懒。他就有n杯卡布奇洛,然后想尽快喝完,每天工作m分钟,每次喝咖啡需要1分钟。然后问最少天数喝完,还要第i杯咖啡第几天喝。
第i杯咖啡,a[i]指的是在a[i]这一个时刻休息,喝上这一杯咖啡。
【题解】:
需要用贪心的想法,就是把最小的时间每隔d分钟,放在同一天,只要不够放那么就用下一天进行这个操作。
最后需要set进行维护,set维护需要学会两个操作,第一个是S.lower_bound(x),如果找到就返回对应的迭代器,如果找不到那么就会返回S.end()。第二个操作就是S.erase()
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+1000;
typedef struct Node{
int No,x;
bool operator < (const Node &t)const {
return x<t.x;
}
}Node;
Node tmp;
set <Node> S;
int n,m,d,cnt,val,pos[N];
int main()
{
scanf("%d%d%d",&n,&m,&d);
for(int i=1;i<=n;i++){
scanf("%d",&tmp.x);
tmp.No = i ;
S.insert(tmp);
}
cnt = 1 ;
Node cur = *S.begin();
pos[cur.No]=cnt;
S.erase( *(S.begin()) );
val = cur.x;
for(int i=2;i<=n;i++){
val = val + d + 1;
tmp.x = val;
auto t=S.lower_bound(tmp);
if( t!=S.end() ){
tmp = *t;
pos[tmp.No] = cnt;
val = tmp.x;
S.erase(t);
continue;
}
else{
t = S.begin();
tmp = *t;
pos[tmp.No] =++cnt;
val = tmp.x;
S.erase(t);
}
}
printf("%d\n",cnt);
for(int i=1;i<=n;i++){
printf("%d%c",pos[i],i==n?'\n':' ');
}
return 0;
}
B_Glider
【题意】:
看图说话,你可以选择任意一个位置的高度h,开始驾驶飞机,然后有n个平流层,这个平流层可以让飞机不掉下来。问最后能飞到最远的距离是哪里??
【题解】:
大家首先需要一波预处理,后来你会发现,枚举n段平流层的最开始的位置,然后用二分解决问题。
预处理内容,两端平流层之间的距离,处理成前缀和,然后看看最后的会在那一段之间降落。
具体看代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
typedef long long ll;
typedef struct Node{
ll L,R;
}Node;
int Lowerbound(int x){
int L=x,R=n,ans=x;
while(L<=R){
int mid = (L+R)/2;
if( sum[mid]-sum[x] < h ){
ans = mid;
L = mid+1;
}else{
R = mid-1;
}
}
return ans;
}
Node a[N];
ll n,h,p[N],sum[N];
int main()
{
scanf("%lld%lld",&n,&h);
for(int i=0;i<n;i++){
scanf("%lld%lld",&a[i].L,&a[i].R);
if(i>0){
p[i]=a[i].L-a[i-1].R;
sum[i]=sum[i-1]+p[i];
}
}
p[n]=h+1;
sum[n]=sum[n-1]+p[n];
ll ans=0,tmp;
for(int i=0;i<n;i++){
int pos=Lowerbound(i);
//printf("####%d\n",pos);
tmp = ( h-(sum[pos]-sum[i])+a[pos].R )-a[i].L;
ans=max(ans,tmp);
}
printf("%lld\n",ans);
return 0;
}
C_Bacteria
【题意】:
博士想让所有的细菌合体,然后他可以购买不同大小的细菌,合体的规则是两个同样大小的细菌可以合成一个两倍大的细菌。然后,后来发现这个能不能合成一个细菌,主要是看是不是二进制数。
【题解】:
首先你会得知这个题目是跟二进制有关,然后发现最小的个数求法,就是把所有的细菌变成一个数字,然后化简成二进制的11010101。
然后最后一个位置的"1",需要ans++,然后一直往前到第二个,"0"的位置都要ans++。
大家用草稿纸操练一下就知道为什么了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+100;
int vis[N];
ll a[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
sort(a+1,a+n+1);
a[0] = a[1];
int flag = 1;
for(int i=1;i<=n;i++){
if(a[i]%a[0]) flag=0;
a[i]/=a[0];
if ( ( a[i]&(-a[i]) ) != a[i] ){
flag=0;
}
}
if( !flag ) {
return 0*printf("-1\n");
}
for(ll i=1;i<=n;i++){
for(ll j=0;j<=33;j++){
if( (1ll<<j) == a[i]){
vis[j]++;break;
}
}
}
int Top = 0;
for(int j = 0 ; j<=63;j++ ){
if(vis[j]){
Top = j ;
}
if(vis[j]>=2){
vis[j+1] += vis[j]/2;
vis[j] %= 2 ;
Top = j ;
}
}
int f=0,ans=0;
/*printf("####%d\n",Top);
for(int i=0;i<=Top;i++){
printf("%d%c",vis[i],i==Top?'\n':' ');
}*/
for(int i=0;i<=Top-1;i++){
if(vis[i]&&!f){
ans++;
f=1;
continue;
}
if( f && vis[i]==0 ){
ans++;
}
}
printf("%d\n",ans);
return 0;
}
D_Masquerade strikes back
【题意】:
就是给定n个数,然后每一个数a[i],然后问把这个数写成两个数相乘的结果,不能重复。
【题解】:
直接根号a[i],直接处理。
具体看代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+100;
typedef struct Node{
int x,No;
int t1,t2;
}Node ;
Node a[N];
bool cmp(Node u,Node v){
if(u.x==v.x)
return u.No<v.No;
return u.x<v.x;
}
bool cmp2(Node u,Node v){
return u.No<v.No;
}
int Div_solve(int x,int St,int cnt){
int tot=0;
for(ll i=1;i*i<=x;i++){
if( x%i==0 ){
if(x/i==i){
a[St+tot].t1=i;
a[St+tot].t2=i;
tot++;
}else{
a[St+tot].t1=i;
a[St+tot].t2=x/i;
a[St+tot+1].t1=x/i;
a[St+tot+1].t2=i;
tot+=2;
}
if(tot>=cnt) break;
}
}
return cnt<=tot;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i].x);
a[i].No = i;
}
sort(a,a+n,cmp);
int S=0,cnt=0,flag=1;
for(int i=0;i<n;i+=cnt){
S=i,cnt=0;
for(int j=i;j<n;j++){
if(a[i].x==a[j].x){
cnt++;
}else {
break;
}
}
flag = Div_solve(a[S].x,S,cnt);
if(flag==0)break;
}
if(!flag){
printf("NO\n");
}else{
printf("YES\n");
sort(a,a+n,cmp2);
for(int i=0;i<n;i++){
printf("%d %d\n",a[i].t1,a[i].t2);
}
}
return 0;
}
E_Painting the Fence
【题意】:
就是给你n个颜色,然后m次操作,每次操作就是根据它的指令给定的颜色,从左往右扫同一个颜色。
【题解】:
不看题解,我都不知道为什么需要用set来维护,直到我知道为什么用set维护,发现set真的强大。
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+100;
int n,m,x,a[N],c[N],vis[N];
set<int>s[N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
s[a[i]].insert(i);
}
scanf("%d",&m);
while(m--){
scanf("%d",&x);
//printf("$$$$$$$$\n");
if( s[x].size() <= 1 || vis[x] ) continue;
//已经被删除完了,或者是值为x的只出现一次,或者已经操作过的
//printf("@@@@@@\n");
int L=*(s[x].begin());
int R=*(s[x].rbegin());
//printf("####\n");
for(int i=L+1;i<=R-1;i++){
s[a[i]].erase(i);
if( s[a[i]].size() >=1 && vis[a[i]] ){
//对于处理过的区间,之后剩下两个端点值,那么肯定在处理当前颜色之间。所以直接删除即可。
//避免了删除发生错误
i=*(s[a[i]].begin());
s[a[i]].erase(i);
}
}
vis[x]=1;
}
for(int i=1;i<N;i++){
if( vis[i] && s[i].size()>=2 ){
int L=*(s[i].begin());
int R=*(s[i].rbegin());
for(int j=L;j<=R;j++){
a[j]=i;
}
}
}
for(int i=1;i<=n;i++){
printf("%d%c",a[i],i==n?'\n':' ');
}
return 0;
}
F_Tickets
【题意】:
题意:有n次询问每次询问给一个x,且x自动补全六位ABCDEF,询问<x且Abs(A+B+C-D-E-F)<x在此计算式下的数目
【题解】:
就是暴力处理即可,离线操作,把每一个差值,存对应的下标,然后用二分处理。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
vector <int> v[30];
int f(int x){
char t[100];
sprintf(t,"%06d",x);
int tmp=0;
for(int i=0;i<3;i++){
tmp+=(t[i]-'0');
}
int tmp2=0;
for(int i=3;i<6;i++){
tmp2+=(t[i]-'0');
}
tmp=abs(tmp-tmp2);
/*if(x>984351&&x<989999){
printf("%s %d\n",t,tmp);
}*/
return tmp;
}
int main()
{
int tmp,n;
for(int i=0;i<N+10;i++){
tmp=f(i);
v[tmp].push_back(i);
}
scanf("%d",&n);
/*for(auto x:v[0]){
if(x<1000)
printf("%d\n",x);
}*/
for(int i=1;i<=n;i++){
int x,ans=0,t,cnt;
scanf("%d",&x);
t=f(x);
//printf(" T %d\n",t);
for(int i=0;i<t;i++){
//printf("V[%d] = Size %d\n",i,v[i].size());
cnt=lower_bound(v[i].begin(),v[i].end(),x)-v[i].begin();
ans+=cnt;
}
printf("%d\n",ans);
}
return 0;
}
G. Tree Reconstruction
题意:要求构造一颗树,这棵树n-1条要求,每个要求需要使得断开树中的一条边后分成的两个连通块中的最大下标值分别为a和b
思路:首先a,b之中必须要有一个值为n,因为两个连通块中至少有一个连通块中有n,然后将所有a取出来排序,用set维护未被使用掉的节点值,贪心构造这颗树就可以了
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int a[N],ans[N],tot=0;
int main()
{
int n,flag=1;
scanf("%d",&n);
for(int i=1,u,t;i<n;i++){
scanf("%d%d",&a[i],&t);
flag &= (t==n);
}
sort(a+1,a+n);
set < int > S;
for (int i=1; i<=n ; i++){
S.insert(i);
}
ans[1] = a[1];
S.erase ( a[1] );
int cnt=2;
for(int i = 2 ; i <= n ;i++){
if ( a[i] != a[i-1] ){
ans[ i ] = a[i];
S.erase ( a[i] );
}else{
if( a[i] > *(S.begin()) ){
ans[ i ] = *( S.begin() );
S.erase( S.begin() );
}else {
flag=0;
break;
}
}
}
ans[n]=n;
if(!flag){
puts("NO");
}else{
puts("YES");
for( int i=1; i<n;i++){
printf("%d %d\n",ans[i],ans[i+1]);
}
}
return 0;
}
H题 Theater Square
题意:有一个矩形,挖掉一个小矩形之后用1*2的方块填充这个矩形,填充不完整的地方(会留有一些1*1)需要将1*2的方块打碎成两块用来填充,问打碎多少块1*2的方块
水题:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
int x[3],y[3];
for(int i=1;i<=2;i++)
scanf("%d%d",&x[i],&y[i]);
int One = 0;
if ( x[1] > 1 ){
if( m & 1 )
One += x[1] - 1;
}
if ( x[2] < n ){
if( m & 1)
One += n - x[2] ;
}
if ( y[1] > 1 ){
if((y[1]-1)&1){
One += (x[2]-x[1]+1);
}
}
if ( y[2] < m ){
if( (m-y[2])&1 ){
One += (x[2]-x[1]+1);
}
}
int ans=0;
if(One)
ans = (One - 1)/2+1;
printf("%d\n",ans);
return 0;
}
I_Heist
【水题】:计算最大值-最小值+1-n
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+100;
int a[N];
int main()
{
int n,ans;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
sort(a,a+n);
ans = (a[n-1] - a[0]+1)-n;
printf("%d\n",ans);
return 0;
}
J_Buying a TV Set
题意:求W<=A且H<=B使得W/H==X/Y的组数
思路:将X和Y约分后取Min(A/X,B/Y)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd( ll a, ll b){
return a%b==0 ? b : gcd( b, a%b );
}
int main()
{
ll w,h,x,y;
scanf("%lld%lld%lld%lld",&w,&h,&x,&y);
ll Gcd = gcd (x,y);
x /= Gcd ;
y /= Gcd ;
ll ans = 0 ;
ans = max( ans, min(w/x,h/y));
printf("%lld\n",ans);
return 0;
}
K_Medians and Partition
【题意】:
最多可以分成多少份,可以让每一份的中位数大于等于m
【题解】:
简直就是一个思维题,如果有一个小于m的,必须要有两个大于等于m的。
最后就变成的有一个大于就++,有一个小于的--.
最后答案就是:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m,x,ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&x);
if(x>=m) ans++;
else ans--;
}
ans=max(ans,0);
printf("%d\n",ans);
return 0;
}
L_Ray in the tube
【题意】:
可以两个任意一块镜面的某一个点进行出发,然后以某一个角度进行反射。
最后达到的效果达到的点就是越多越好。
【题解】:
看网上结论就是直接枚举所有二进制的数,然后在mod的意义上最多的一组就是答案。
思路:因为在枚举每个步长之后其奇数倍的步长就不需要再枚举了,所以枚举从下镜面当上镜面的变化步长只需枚举logn个,维护相应的数据答案,注意光线是可以垂直的
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
ll n,m,y,ans = 2 ;
ll a[N],b[N];
ll solve( int x){
ll mod = 1ll << (x+1);
map< ll,ll > Mp1,Mp2;
for(int i=1;i<=n;i++){
Mp1[ a[i]%mod ] ++ ;
}
for(int i=1;i<=m;i++){
Mp2[ b[i]%mod ] ++ ;
}
for(int i=1;i<=n;i++){
ans =max( ans ,Mp1[ a[i]%mod ] + Mp2[(a[i]+(1ll<<x))%mod]);
}
for(int i=1;i<=m;i++){
ans =max( ans ,Mp2[ b[i]%mod ] + Mp1[(b[i]+(1ll<<x))%mod]);
}
}
int main()
{
scanf("%lld%lld",&n,&y);
for(int i=1 ;i<=n;i++){
scanf("%lld",&a[i]);
}
scanf("%lld%lld",&m,&y);
for(int i=1; i<=m;i++){
scanf("%lld",&b[i]);
}
for(int i = 0; i<=31;i++){
ans = max( ans, solve(i) );
}
printf("%lld\n",ans);
return 0;
}